Merge pull request #641 from litghost/initial_lookahead
Initial lookahead for FPGA interchange.
This commit is contained in:
commit
4d8dcab1d3
2
3rdparty/fpga-interchange-schema
vendored
2
3rdparty/fpga-interchange-schema
vendored
@ -1 +1 @@
|
||||
Subproject commit cb6d16847dfca7f104205c83bbdc056303ac82a0
|
||||
Subproject commit 8ec5b1739d7b91b84df3e92ccc70c7a7ee644089
|
@ -43,6 +43,10 @@ if(WASI)
|
||||
)
|
||||
else()
|
||||
set(USE_THREADS ON)
|
||||
find_package(TBB QUIET)
|
||||
if (TBB_FOUND)
|
||||
add_definitions(-DNEXTPNR_USE_TBB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT USE_THREADS)
|
||||
@ -243,6 +247,9 @@ endif()
|
||||
if(PROFILER)
|
||||
list(APPEND EXTRA_LIB_DEPS profiler)
|
||||
endif()
|
||||
if(TBB_FOUND)
|
||||
list(APPEND EXTRA_LIB_DEPS tbb)
|
||||
endif()
|
||||
|
||||
foreach (family ${ARCH})
|
||||
message(STATUS "Configuring architecture: ${family}")
|
||||
|
@ -24,9 +24,11 @@
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/uuid/detail/sha1.hpp>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <queue>
|
||||
|
||||
#include "constraints.impl.h"
|
||||
#include "fpga_interchange.h"
|
||||
#include "log.h"
|
||||
@ -42,6 +44,11 @@
|
||||
// Include tcl.h late because it messed with defines and let them leave the
|
||||
// scope of the header.
|
||||
#include <tcl.h>
|
||||
|
||||
//#define DEBUG_BINDING
|
||||
//#define USE_LOOKAHEAD
|
||||
//#define DEBUG_CELL_PIN_MAPPING
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
struct SiteBelPair
|
||||
{
|
||||
@ -83,6 +90,22 @@ void IdString::initialize_arch(const BaseCtx *ctx) {}
|
||||
|
||||
static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }
|
||||
|
||||
static std::string sha1_hash(const char *data, size_t size)
|
||||
{
|
||||
boost::uuids::detail::sha1 hasher;
|
||||
hasher.process_bytes(data, size);
|
||||
|
||||
// unsigned int[5]
|
||||
boost::uuids::detail::sha1::digest_type digest;
|
||||
hasher.get_digest(digest);
|
||||
|
||||
std::ostringstream buf;
|
||||
for (int i = 0; i < 5; ++i)
|
||||
buf << std::hex << std::setfill('0') << std::setw(8) << digest[i];
|
||||
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
Arch::Arch(ArchArgs args) : args(args)
|
||||
{
|
||||
try {
|
||||
@ -90,6 +113,8 @@ Arch::Arch(ArchArgs args) : args(args)
|
||||
if (args.chipdb.empty() || !blob_file.is_open())
|
||||
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
|
||||
const char *blob = reinterpret_cast<const char *>(blob_file.data());
|
||||
|
||||
chipdb_hash = sha1_hash(blob, blob_file.size());
|
||||
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(blob));
|
||||
} catch (...) {
|
||||
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
|
||||
@ -249,7 +274,13 @@ Arch::Arch(ArchArgs args) : args(args)
|
||||
default_tags.resize(max_tag_count);
|
||||
}
|
||||
|
||||
void Arch::init() { dedicated_interconnect.init(getCtx()); }
|
||||
void Arch::init()
|
||||
{
|
||||
#ifdef USE_LOOKAHEAD
|
||||
lookahead.init(getCtx(), getCtx());
|
||||
#endif
|
||||
dedicated_interconnect.init(getCtx());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@ -894,18 +925,11 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
|
||||
|
||||
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||
{
|
||||
// FIXME: Implement something to push the A* router in the right direction.
|
||||
int src_x, src_y;
|
||||
get_tile_x_y(src.tile, &src_x, &src_y);
|
||||
|
||||
int dst_x, dst_y;
|
||||
get_tile_x_y(dst.tile, &dst_x, &dst_y);
|
||||
|
||||
delay_t base = 30 * std::min(std::abs(dst_x - src_x), 18) + 10 * std::max(std::abs(dst_x - src_x) - 18, 0) +
|
||||
60 * std::min(std::abs(dst_y - src_y), 6) + 20 * std::max(std::abs(dst_y - src_y) - 6, 0) + 300;
|
||||
|
||||
base = (base * 3) / 2;
|
||||
return base;
|
||||
#ifdef USE_LOOKAHEAD
|
||||
return lookahead.estimateDelay(getCtx(), src, dst);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
||||
@ -1757,6 +1781,8 @@ bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const
|
||||
|
||||
bool Arch::checkPipAvail(PipId pip) const { return checkPipAvailForNet(pip, nullptr); }
|
||||
|
||||
std::string Arch::get_chipdb_hash() const { return chipdb_hash; }
|
||||
|
||||
// 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);
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "arch_iterators.h"
|
||||
#include "chipdb.h"
|
||||
#include "dedicated_interconnect.h"
|
||||
#include "lookahead.h"
|
||||
#include "site_router.h"
|
||||
#include "site_routing_cache.h"
|
||||
|
||||
@ -45,6 +46,8 @@ struct ArchArgs
|
||||
{
|
||||
std::string chipdb;
|
||||
std::string package;
|
||||
bool rebuild_lookahead;
|
||||
bool dont_write_lookahead;
|
||||
};
|
||||
|
||||
struct ArchRanges
|
||||
@ -1038,8 +1041,13 @@ 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();
|
||||
|
||||
Lookahead lookahead;
|
||||
mutable RouteNodeStorage node_storage;
|
||||
mutable SiteRoutingCache site_routing_cache;
|
||||
|
||||
std::string chipdb_hash;
|
||||
std::string get_chipdb_hash() const;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
400
fpga_interchange/cost_map.cc
Normal file
400
fpga_interchange/cost_map.cc
Normal file
@ -0,0 +1,400 @@
|
||||
/*
|
||||
* 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 "cost_map.h"
|
||||
|
||||
#include "context.h"
|
||||
#include "log.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
///@brief Factor to adjust the penalty calculation for deltas outside the segment bounding box:
|
||||
// factor < 1.0: penalty has less impact on the final returned delay
|
||||
// factor > 1.0: penalty has more impact on the final returned delay
|
||||
static constexpr float PENALTY_FACTOR = 1.f;
|
||||
|
||||
///@brief Minimum penalty cost that is added when penalizing a delta outside the segment bounding box.
|
||||
static constexpr delay_t PENALTY_MIN = 1;
|
||||
|
||||
// also known as the L1 norm
|
||||
static int manhattan_distance(const std::pair<int32_t, int32_t> &a, const std::pair<int32_t, int32_t> &b)
|
||||
{
|
||||
return std::abs(b.first - a.first) + std::abs(b.second - a.second);
|
||||
}
|
||||
|
||||
static delay_t penalize(const delay_t &entry, int distance, delay_t penalty)
|
||||
{
|
||||
penalty = std::max(penalty, PENALTY_MIN);
|
||||
return entry + distance * penalty * PENALTY_FACTOR;
|
||||
}
|
||||
|
||||
delay_t CostMap::get_delay(const Context *ctx, WireId src_wire, WireId dst_wire) const
|
||||
{
|
||||
TypeWirePair type_pair;
|
||||
type_pair.src = TypeWireId(ctx, src_wire);
|
||||
type_pair.dst = TypeWireId(ctx, dst_wire);
|
||||
|
||||
int src_tile;
|
||||
if (src_wire.tile == -1) {
|
||||
src_tile = ctx->chip_info->nodes[src_wire.index].tile_wires[0].tile;
|
||||
} else {
|
||||
src_tile = src_wire.tile;
|
||||
}
|
||||
|
||||
int32_t src_x, src_y;
|
||||
ctx->get_tile_x_y(src_tile, &src_x, &src_y);
|
||||
|
||||
int dst_tile;
|
||||
if (dst_wire.tile == -1) {
|
||||
dst_tile = ctx->chip_info->nodes[dst_wire.index].tile_wires[0].tile;
|
||||
} else {
|
||||
dst_tile = dst_wire.tile;
|
||||
}
|
||||
|
||||
int32_t dst_x, dst_y;
|
||||
ctx->get_tile_x_y(dst_tile, &dst_x, &dst_y);
|
||||
|
||||
auto iter = cost_map_.find(type_pair);
|
||||
if (iter == cost_map_.end()) {
|
||||
auto &src_type = ctx->chip_info->tile_types[type_pair.src.type];
|
||||
IdString src_tile_type(src_type.name);
|
||||
IdString src_wire_name(src_type.wire_data[type_pair.src.index].name);
|
||||
|
||||
auto &dst_type = ctx->chip_info->tile_types[type_pair.dst.type];
|
||||
IdString dst_tile_type(dst_type.name);
|
||||
IdString dst_wire_name(dst_type.wire_data[type_pair.dst.index].name);
|
||||
|
||||
#if 0
|
||||
log_warning("Delay matrix is missing %s/%s -> %s/%s\n",
|
||||
src_tile_type.c_str(ctx),
|
||||
src_wire_name.c_str(ctx),
|
||||
dst_tile_type.c_str(ctx),
|
||||
dst_wire_name.c_str(ctx));
|
||||
#endif
|
||||
return std::numeric_limits<delay_t>::max();
|
||||
}
|
||||
|
||||
const auto &delay_matrix = iter->second;
|
||||
|
||||
int32_t off_x = delay_matrix.offset.first + (dst_x - src_x);
|
||||
int32_t off_y = delay_matrix.offset.second + (dst_y - src_y);
|
||||
|
||||
int32_t x_dim = delay_matrix.data.shape()[0];
|
||||
int32_t y_dim = delay_matrix.data.shape()[1];
|
||||
NPNR_ASSERT(x_dim > 0);
|
||||
NPNR_ASSERT(y_dim > 0);
|
||||
|
||||
// Bound closest_x/y to [0, dim)
|
||||
int32_t closest_x = std::min(std::max(off_x, 0), x_dim - 1);
|
||||
int32_t closest_y = std::min(std::max(off_y, 0), y_dim - 1);
|
||||
|
||||
// Get the cost entry from the cost map at the deltas values
|
||||
auto cost = delay_matrix.data[closest_x][closest_y];
|
||||
NPNR_ASSERT(cost >= 0);
|
||||
|
||||
// Get the base penalty corresponding to the current segment.
|
||||
auto penalty = delay_matrix.penalty;
|
||||
|
||||
// Get the distance between the closest point in the bounding box and the original coordinates.
|
||||
// Note that if the original coordinates are within the bounding box, the distance will be equal to zero.
|
||||
auto distance = manhattan_distance(std::make_pair(off_x, off_y), std::make_pair(closest_x, closest_y));
|
||||
|
||||
// Return the penalized cost (no penalty is added if the coordinates are within the bounding box).
|
||||
return penalize(cost, distance, penalty);
|
||||
}
|
||||
|
||||
void CostMap::set_cost_map(const Context *ctx, const TypeWirePair &wire_pair,
|
||||
const HashTables::HashMap<std::pair<int32_t, int32_t>, delay_t> &delays)
|
||||
{
|
||||
CostMapEntry delay_matrix;
|
||||
|
||||
auto &offset = delay_matrix.offset;
|
||||
offset.first = 0;
|
||||
offset.second = 0;
|
||||
|
||||
int32_t max_x_offset = 0;
|
||||
int32_t max_y_offset = 0;
|
||||
|
||||
for (const auto &delay_pair : delays) {
|
||||
auto &dx_dy = delay_pair.first;
|
||||
offset.first = std::max(-dx_dy.first, offset.first);
|
||||
offset.second = std::max(-dx_dy.second, offset.second);
|
||||
max_x_offset = std::max(dx_dy.first, max_x_offset);
|
||||
max_y_offset = std::max(dx_dy.second, max_y_offset);
|
||||
}
|
||||
|
||||
int32_t x_dim = offset.first + max_x_offset + 1;
|
||||
int32_t y_dim = offset.second + max_y_offset + 1;
|
||||
|
||||
delay_matrix.data.resize(boost::extents[x_dim][y_dim]);
|
||||
|
||||
// Fill matrix with sentinel of -1 to know where the holes in the matrix
|
||||
// are.
|
||||
std::fill_n(delay_matrix.data.data(), delay_matrix.data.num_elements(), -1);
|
||||
|
||||
for (const auto &delay_pair : delays) {
|
||||
auto &dx_dy = delay_pair.first;
|
||||
int32_t off_x = dx_dy.first + offset.first;
|
||||
int32_t off_y = dx_dy.second + offset.second;
|
||||
NPNR_ASSERT(off_x >= 0);
|
||||
NPNR_ASSERT(off_x < x_dim);
|
||||
NPNR_ASSERT(off_y >= 0);
|
||||
NPNR_ASSERT(off_y < y_dim);
|
||||
|
||||
delay_matrix.data[off_x][off_y] = delay_pair.second;
|
||||
}
|
||||
|
||||
delay_matrix.penalty = get_penalty(delay_matrix.data);
|
||||
fill_holes(ctx, wire_pair, delay_matrix.data, delay_matrix.penalty);
|
||||
|
||||
{
|
||||
cost_map_mutex_.lock();
|
||||
auto result = cost_map_.emplace(wire_pair, delay_matrix);
|
||||
NPNR_ASSERT(result.second);
|
||||
cost_map_mutex_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static void assign_min_entry(delay_t *dst, const delay_t &src)
|
||||
{
|
||||
if (src >= 0) {
|
||||
if (*dst < 0) {
|
||||
*dst = src;
|
||||
} else if (src < *dst) {
|
||||
*dst = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<delay_t, int> CostMap::get_nearby_cost_entry(const boost::multi_array<delay_t, 2> &matrix, int cx, int cy,
|
||||
const ArcBounds &bounds)
|
||||
{
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("Filling %d, %d within (%d, %d, %d, %d)\n", cx, cy, bounds.x0, bounds.y0, bounds.x1, bounds.y1);
|
||||
#endif
|
||||
|
||||
// spiral around (cx, cy) looking for a nearby entry
|
||||
bool in_bounds = bounds.contains(cx, cy);
|
||||
if (!in_bounds) {
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("Already out of bounds, return!\n");
|
||||
#endif
|
||||
return std::make_pair(-1, 0);
|
||||
}
|
||||
int n = 0;
|
||||
delay_t fill(matrix[cx][cy]);
|
||||
|
||||
while (in_bounds && (fill < 0)) {
|
||||
n++;
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("At n = %d\n", n);
|
||||
#endif
|
||||
in_bounds = false;
|
||||
delay_t min_entry = -1;
|
||||
for (int ox = -n; ox <= n; ox++) {
|
||||
int x = cx + ox;
|
||||
int oy = n - abs(ox);
|
||||
int yp = cy + oy;
|
||||
int yn = cy - oy;
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("Testing %d, %d\n", x, yp);
|
||||
#endif
|
||||
if (bounds.contains(x, yp)) {
|
||||
assign_min_entry(&min_entry, matrix[x][yp]);
|
||||
in_bounds = true;
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("matrix[%d, %d] = %d, min_entry = %d\n", x, yp, matrix[x][yp], min_entry);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("Testing %d, %d\n", x, yn);
|
||||
#endif
|
||||
if (bounds.contains(x, yn)) {
|
||||
assign_min_entry(&min_entry, matrix[x][yn]);
|
||||
in_bounds = true;
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("matrix[%d, %d] = %d, min_entry = %d\n", x, yn, matrix[x][yn], min_entry);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (fill < 0 && min_entry >= 0) {
|
||||
fill = min_entry;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(fill, n);
|
||||
}
|
||||
|
||||
void CostMap::fill_holes(const Context *ctx, const TypeWirePair &type_pair, boost::multi_array<delay_t, 2> &matrix,
|
||||
delay_t delay_penalty)
|
||||
{
|
||||
// find missing cost entries and fill them in by copying a nearby cost entry
|
||||
std::vector<std::tuple<unsigned, unsigned, delay_t>> missing;
|
||||
bool couldnt_fill = false;
|
||||
auto shifted_bounds = ArcBounds(0, 0, matrix.shape()[0] - 1, matrix.shape()[1] - 1);
|
||||
int max_fill = 0;
|
||||
for (unsigned ix = 0; ix < matrix.shape()[0]; ix++) {
|
||||
for (unsigned iy = 0; iy < matrix.shape()[1]; iy++) {
|
||||
delay_t &cost_entry = matrix[ix][iy];
|
||||
if (cost_entry < 0) {
|
||||
// maximum search radius
|
||||
delay_t filler;
|
||||
int distance;
|
||||
std::tie(filler, distance) = get_nearby_cost_entry(matrix, ix, iy, shifted_bounds);
|
||||
if (filler >= 0) {
|
||||
missing.push_back(std::make_tuple(ix, iy, penalize(filler, distance, delay_penalty)));
|
||||
max_fill = std::max(max_fill, distance);
|
||||
} else {
|
||||
couldnt_fill = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (couldnt_fill) {
|
||||
// give up trying to fill an empty matrix
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!couldnt_fill && max_fill > 0) {
|
||||
if (ctx->verbose) {
|
||||
auto &src_type_data = ctx->chip_info->tile_types[type_pair.src.type];
|
||||
IdString src_type(src_type_data.name);
|
||||
IdString src_wire(src_type_data.wire_data[type_pair.src.index].name);
|
||||
|
||||
auto &dst_type_data = ctx->chip_info->tile_types[type_pair.dst.type];
|
||||
IdString dst_type(dst_type_data.name);
|
||||
IdString dst_wire(dst_type_data.wire_data[type_pair.dst.index].name);
|
||||
|
||||
#ifdef DEBUG_FILL
|
||||
log_info("At %s/%s -> %s/%s: max_fill = %d, delay_penalty = %d\n", src_type.c_str(ctx), src_wire.c_str(ctx),
|
||||
dst_type.c_str(ctx), dst_wire.c_str(ctx), max_fill, delay_penalty);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// write back the missing entries
|
||||
for (auto &xy_entry : missing) {
|
||||
matrix[std::get<0>(xy_entry)][std::get<1>(xy_entry)] = std::get<2>(xy_entry);
|
||||
}
|
||||
|
||||
if (couldnt_fill) {
|
||||
auto &src_type_data = ctx->chip_info->tile_types[type_pair.src.type];
|
||||
IdString src_type(src_type_data.name);
|
||||
IdString src_wire(src_type_data.wire_data[type_pair.src.index].name);
|
||||
|
||||
auto &dst_type_data = ctx->chip_info->tile_types[type_pair.dst.type];
|
||||
IdString dst_type(dst_type_data.name);
|
||||
IdString dst_wire(dst_type_data.wire_data[type_pair.dst.index].name);
|
||||
|
||||
log_warning("Couldn't fill holes in the cost matrix %s/%s -> %s/%s %d x %d bounding box\n", src_type.c_str(ctx),
|
||||
src_wire.c_str(ctx), dst_type.c_str(ctx), dst_wire.c_str(ctx), shifted_bounds.x1,
|
||||
shifted_bounds.y1);
|
||||
for (unsigned y = 0; y < matrix.shape()[1]; y++) {
|
||||
for (unsigned x = 0; x < matrix.shape()[0]; x++) {
|
||||
NPNR_ASSERT(matrix[x][y] >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delay_t CostMap::get_penalty(const boost::multi_array<delay_t, 2> &matrix) const
|
||||
{
|
||||
delay_t min_delay = std::numeric_limits<delay_t>::max();
|
||||
delay_t max_delay = std::numeric_limits<delay_t>::min();
|
||||
|
||||
std::pair<int32_t, int32_t> min_location(0, 0), max_location(0, 0);
|
||||
for (unsigned ix = 0; ix < matrix.shape()[0]; ix++) {
|
||||
for (unsigned iy = 0; iy < matrix.shape()[1]; iy++) {
|
||||
const delay_t &cost_entry = matrix[ix][iy];
|
||||
if (cost_entry >= 0) {
|
||||
if (cost_entry < min_delay) {
|
||||
min_delay = cost_entry;
|
||||
min_location = std::make_pair(ix, iy);
|
||||
}
|
||||
if (cost_entry > max_delay) {
|
||||
max_delay = cost_entry;
|
||||
max_location = std::make_pair(ix, iy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delay_t delay_penalty =
|
||||
(max_delay - min_delay) / static_cast<float>(std::max(1, manhattan_distance(max_location, min_location)));
|
||||
|
||||
return delay_penalty;
|
||||
}
|
||||
|
||||
void CostMap::from_reader(lookahead_storage::CostMap::Reader reader)
|
||||
{
|
||||
for (auto cost_entry : reader.getCostMap()) {
|
||||
TypeWirePair key(cost_entry.getKey());
|
||||
|
||||
auto result = cost_map_.emplace(key, CostMapEntry());
|
||||
NPNR_ASSERT(result.second);
|
||||
|
||||
CostMapEntry &entry = result.first->second;
|
||||
auto data = cost_entry.getData();
|
||||
auto in_iter = data.begin();
|
||||
|
||||
entry.data.resize(boost::extents[cost_entry.getXDim()][cost_entry.getYDim()]);
|
||||
if (entry.data.num_elements() != data.size()) {
|
||||
log_error("entry.data.num_elements() %zu != data.size() %u", entry.data.num_elements(), data.size());
|
||||
}
|
||||
|
||||
delay_t *out = entry.data.origin();
|
||||
for (; in_iter != data.end(); ++in_iter, ++out) {
|
||||
*out = *in_iter;
|
||||
}
|
||||
|
||||
entry.penalty = cost_entry.getPenalty();
|
||||
|
||||
entry.offset.first = cost_entry.getXOffset();
|
||||
entry.offset.second = cost_entry.getYOffset();
|
||||
}
|
||||
}
|
||||
|
||||
void CostMap::to_builder(lookahead_storage::CostMap::Builder builder) const
|
||||
{
|
||||
auto cost_map = builder.initCostMap(cost_map_.size());
|
||||
auto entry_iter = cost_map.begin();
|
||||
auto in = cost_map_.begin();
|
||||
for (; entry_iter != cost_map.end(); ++entry_iter, ++in) {
|
||||
NPNR_ASSERT(in != cost_map_.end());
|
||||
|
||||
in->first.to_builder(entry_iter->getKey());
|
||||
const CostMapEntry &entry = in->second;
|
||||
|
||||
auto data = entry_iter->initData(entry.data.num_elements());
|
||||
const delay_t *data_in = entry.data.origin();
|
||||
for (size_t i = 0; i < entry.data.num_elements(); ++i) {
|
||||
data.set(i, data_in[i]);
|
||||
}
|
||||
|
||||
entry_iter->setXDim(entry.data.shape()[0]);
|
||||
entry_iter->setYDim(entry.data.shape()[1]);
|
||||
entry_iter->setXOffset(entry.offset.first);
|
||||
entry_iter->setYOffset(entry.offset.second);
|
||||
entry_iter->setPenalty(entry.penalty);
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
68
fpga_interchange/cost_map.h
Normal file
68
fpga_interchange/cost_map.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 COST_MAP_H
|
||||
#define COST_MAP_H
|
||||
|
||||
#include <boost/multi_array.hpp>
|
||||
#include <mutex>
|
||||
|
||||
#include "hash_table.h"
|
||||
#include "lookahead.capnp.h"
|
||||
#include "nextpnr_namespaces.h"
|
||||
#include "nextpnr_types.h"
|
||||
#include "type_wire.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct Context;
|
||||
|
||||
class CostMap
|
||||
{
|
||||
public:
|
||||
delay_t get_delay(const Context *ctx, WireId src, WireId dst) const;
|
||||
void set_cost_map(const Context *ctx, const TypeWirePair &wire_pair,
|
||||
const HashTables::HashMap<std::pair<int32_t, int32_t>, delay_t> &delays);
|
||||
|
||||
void from_reader(lookahead_storage::CostMap::Reader reader);
|
||||
void to_builder(lookahead_storage::CostMap::Builder builder) const;
|
||||
|
||||
private:
|
||||
struct CostMapEntry
|
||||
{
|
||||
boost::multi_array<delay_t, 2> data;
|
||||
std::pair<int32_t, int32_t> offset;
|
||||
delay_t penalty;
|
||||
};
|
||||
|
||||
std::mutex cost_map_mutex_;
|
||||
HashTables::HashMap<TypeWirePair, CostMapEntry> cost_map_;
|
||||
|
||||
void fill_holes(const Context *ctx, const TypeWirePair &wire_pair, boost::multi_array<delay_t, 2> &matrix,
|
||||
delay_t delay_penality);
|
||||
|
||||
std::pair<delay_t, int> get_nearby_cost_entry(const boost::multi_array<delay_t, 2> &matrix, int cx, int cy,
|
||||
const ArcBounds &bounds);
|
||||
delay_t get_penalty(const boost::multi_array<delay_t, 2> &matrix) const;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif /* COST_MAP_H */
|
@ -24,14 +24,31 @@ add_custom_target(all-${family}-archcheck-tests)
|
||||
add_subdirectory(${family}/examples/devices)
|
||||
add_subdirectory(${family}/examples/tests)
|
||||
|
||||
set(PROTOS lookahead.capnp)
|
||||
set(CAPNP_SRCS)
|
||||
set(CAPNP_HDRS)
|
||||
find_package(CapnProto REQUIRED)
|
||||
foreach (proto ${PROTOS})
|
||||
capnp_generate_cpp(CAPNP_SRC CAPNP_HDR fpga_interchange/${proto})
|
||||
list(APPEND CAPNP_HDRS ${CAPNP_HDR})
|
||||
list(APPEND CAPNP_SRCS ${CAPNP_SRC})
|
||||
endforeach()
|
||||
|
||||
add_library(extra_capnp STATIC ${CAPNP_SRCS})
|
||||
target_link_libraries(extra_capnp PRIVATE CapnProto::capnp)
|
||||
|
||||
target_include_directories(extra_capnp INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/fpga_interchange)
|
||||
|
||||
foreach (target ${family_targets})
|
||||
target_include_directories(${target} PRIVATE ${TCL_INCLUDE_PATH})
|
||||
target_link_libraries(${target} PRIVATE ${TCL_LIBRARY})
|
||||
target_link_libraries(${target} PRIVATE fpga_interchange_capnp)
|
||||
target_link_libraries(${target} PRIVATE extra_capnp)
|
||||
target_link_libraries(${target} PRIVATE z)
|
||||
endforeach()
|
||||
|
||||
if(BUILD_GUI)
|
||||
target_link_libraries(gui_${family} fpga_interchange_capnp)
|
||||
target_link_libraries(gui_${family} extra_capnp)
|
||||
target_link_libraries(gui_${family} z)
|
||||
endif()
|
||||
|
61
fpga_interchange/lookahead.capnp
Normal file
61
fpga_interchange/lookahead.capnp
Normal file
@ -0,0 +1,61 @@
|
||||
@0x97c69817483d9dea;
|
||||
|
||||
using Cxx = import "/capnp/c++.capnp";
|
||||
$Cxx.namespace("lookahead_storage");
|
||||
|
||||
using DelayType = Int32;
|
||||
|
||||
struct TypeWireId {
|
||||
type @0: Int32;
|
||||
index @1: Int32;
|
||||
}
|
||||
|
||||
struct TypeWirePair {
|
||||
src @0 : TypeWireId;
|
||||
dst @1 : TypeWireId;
|
||||
}
|
||||
|
||||
struct InputSiteWireCost {
|
||||
routeTo @0 : TypeWireId;
|
||||
cost @1 : DelayType;
|
||||
}
|
||||
|
||||
struct InputSiteWireCostMap {
|
||||
key @0 : TypeWireId;
|
||||
value @1 : List(InputSiteWireCost);
|
||||
}
|
||||
|
||||
struct OutputSiteWireCostMap {
|
||||
key @0 : TypeWireId;
|
||||
cheapestRouteFrom @1 : TypeWireId;
|
||||
cost @2 : DelayType;
|
||||
}
|
||||
|
||||
struct SiteToSiteCostMap {
|
||||
key @0 : TypeWirePair;
|
||||
cost @1 : DelayType;
|
||||
}
|
||||
|
||||
struct CostMapEntry {
|
||||
key @0 : TypeWirePair;
|
||||
data @1 : List(DelayType);
|
||||
xDim @2 : UInt32;
|
||||
yDim @3 : UInt32;
|
||||
xOffset @4 : UInt32;
|
||||
yOffset @5 : UInt32;
|
||||
penalty @6 : DelayType;
|
||||
}
|
||||
|
||||
struct CostMap {
|
||||
costMap @0 : List(CostMapEntry);
|
||||
}
|
||||
|
||||
struct Lookahead {
|
||||
|
||||
chipdbHash @0 : Text;
|
||||
inputSiteWires @1 : List(InputSiteWireCostMap);
|
||||
outputSiteWires @2 : List(OutputSiteWireCostMap);
|
||||
siteToSiteCost @3 : List(SiteToSiteCostMap);
|
||||
costMap @4 : CostMap;
|
||||
}
|
||||
|
1548
fpga_interchange/lookahead.cc
Normal file
1548
fpga_interchange/lookahead.cc
Normal file
File diff suppressed because it is too large
Load Diff
99
fpga_interchange/lookahead.h
Normal file
99
fpga_interchange/lookahead.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 LOOKAHEAD_H
|
||||
#define LOOKAHEAD_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "cost_map.h"
|
||||
#include "deterministic_rng.h"
|
||||
#include "hash_table.h"
|
||||
#include "lookahead.capnp.h"
|
||||
#include "nextpnr_namespaces.h"
|
||||
#include "type_wire.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// Lookahead is a routing graph generic lookahead builder and evaluator.
|
||||
//
|
||||
// The lookahead data model is structured into 3 parts:
|
||||
// - Output site wires to routing network cost
|
||||
// - Routing network point to point cost
|
||||
// - Routing network cost to input site wires
|
||||
//
|
||||
// If the lookahead is invoked from a routing wire to a routing wire, only
|
||||
// the point to point cost is used.
|
||||
//
|
||||
// If the lookahead is invoked from an output site wire to a routing wire,
|
||||
// the point to point cost is computed using the cheapest output routing wire
|
||||
// from the current site wire and then returned cost is the sum of the output
|
||||
// cost plus the point to point routing network cost.
|
||||
//
|
||||
// If the lookahead is invoked from a routing wire to an input site wire,
|
||||
// then the cost is the point to point routing cost to the cheapest input
|
||||
// routing wire plus the input routing cost.
|
||||
//
|
||||
// If the lookahead is invoked from an output site wire to an input site wire,
|
||||
// then cost is the sum of each of the 3 parts.
|
||||
struct Lookahead
|
||||
{
|
||||
void init(const Context *, DeterministicRNG *rng);
|
||||
void build_lookahead(const Context *, DeterministicRNG *rng);
|
||||
|
||||
bool read_lookahead(const std::string &chipdb_hash, const std::string &file);
|
||||
void write_lookahead(const std::string &chipdb_hash, const std::string &file) const;
|
||||
bool from_reader(const std::string &chipdb_hash, lookahead_storage::Lookahead::Reader reader);
|
||||
void to_builder(const std::string &chipdb_hash, lookahead_storage::Lookahead::Builder builder) const;
|
||||
|
||||
delay_t estimateDelay(const Context *, WireId src, WireId dst) const;
|
||||
|
||||
struct InputSiteWireCost
|
||||
{
|
||||
// This wire is the cheapest non-site wire that leads to this site
|
||||
// wire.
|
||||
TypeWireId route_to;
|
||||
|
||||
// This is the cost from the cheapest_route_to wire to the site wire in
|
||||
// question.
|
||||
delay_t cost;
|
||||
};
|
||||
|
||||
struct OutputSiteWireCost
|
||||
{
|
||||
// This wire is the cheapest non-site wire that is reachable from
|
||||
// this site wire.
|
||||
TypeWireId cheapest_route_from;
|
||||
|
||||
// This is the cost from the site wire in question to
|
||||
// cheapest_route_from wire.
|
||||
delay_t cost;
|
||||
};
|
||||
|
||||
HashTables::HashMap<TypeWireId, std::vector<InputSiteWireCost>> input_site_wires;
|
||||
HashTables::HashMap<TypeWireId, OutputSiteWireCost> output_site_wires;
|
||||
HashTables::HashMap<TypeWirePair, delay_t> site_to_site_cost;
|
||||
CostMap cost_map;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif /* LOOKAHEAD_H */
|
@ -55,6 +55,8 @@ po::options_description FpgaInterchangeCommandHandler::getArchOptions()
|
||||
specific.add_options()("netlist", po::value<std::string>(), "FPGA interchange logical netlist to read");
|
||||
specific.add_options()("phys", po::value<std::string>(), "FPGA interchange Physical netlist to write");
|
||||
specific.add_options()("package", po::value<std::string>(), "Package to use");
|
||||
specific.add_options()("rebuild-lookahead", "Ignore lookahead cache and rebuild");
|
||||
specific.add_options()("dont-write-lookahead", "Don't write the lookahead file");
|
||||
|
||||
return specific;
|
||||
}
|
||||
@ -72,6 +74,9 @@ std::unique_ptr<Context> FpgaInterchangeCommandHandler::createContext(std::unord
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
ArchArgs chipArgs;
|
||||
chipArgs.rebuild_lookahead = vm.count("rebuild_lookahead") != 0;
|
||||
chipArgs.dont_write_lookahead = vm.count("dont_write_lookahead") != 0;
|
||||
|
||||
if (!vm.count("chipdb")) {
|
||||
log_error("chip database binary must be provided\n");
|
||||
}
|
||||
|
174
fpga_interchange/sampler.cc
Normal file
174
fpga_interchange/sampler.cc
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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 "sampler.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
static size_t partition_x(std::vector<size_t>::iterator begin, std::vector<size_t>::iterator end,
|
||||
const std::vector<std::pair<int32_t, int32_t>> &samples)
|
||||
{
|
||||
if (std::distance(begin, end) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the median x value.
|
||||
std::vector<int32_t> xs;
|
||||
xs.reserve(std::distance(begin, end));
|
||||
|
||||
for (auto iter = begin; iter != end; ++iter) {
|
||||
xs.push_back(samples[*iter].first);
|
||||
}
|
||||
|
||||
std::sort(xs.begin(), xs.end());
|
||||
xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
|
||||
|
||||
// Partion on the median x value (e.g. 50% of samples on one side and
|
||||
// 50% of samples on the other side).
|
||||
int32_t x_div = xs[(xs.size() - 1) / 2];
|
||||
|
||||
auto split = std::partition(begin, end,
|
||||
[x_div, &samples](size_t index) -> bool { return samples[index].first <= x_div; });
|
||||
|
||||
return std::distance(begin, split);
|
||||
}
|
||||
|
||||
/* Don't both splitting when the partition has less than kMinSplit. */
|
||||
static constexpr ptrdiff_t kMinSplit = 20;
|
||||
|
||||
static size_t partition_y(std::vector<size_t>::iterator begin, std::vector<size_t>::iterator end,
|
||||
const std::vector<std::pair<int32_t, int32_t>> &samples)
|
||||
{
|
||||
if (std::distance(begin, end) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<int32_t> ys;
|
||||
ys.reserve(std::distance(begin, end));
|
||||
|
||||
for (auto iter = begin; iter != end; ++iter) {
|
||||
ys.push_back(samples[*iter].second);
|
||||
}
|
||||
|
||||
std::sort(ys.begin(), ys.end());
|
||||
ys.erase(std::unique(ys.begin(), ys.end()), ys.end());
|
||||
|
||||
int32_t y_div = ys[(ys.size() - 1) / 2];
|
||||
|
||||
auto split = std::partition(begin, end,
|
||||
[y_div, &samples](size_t index) -> bool { return samples[index].second <= y_div; });
|
||||
|
||||
return std::distance(begin, split);
|
||||
}
|
||||
|
||||
static void add_split(std::vector<size_t> *splits, size_t new_split)
|
||||
{
|
||||
if (splits->back() < new_split) {
|
||||
splits->push_back(new_split);
|
||||
} else if (splits->back() != new_split) {
|
||||
throw std::runtime_error("Split is not consectutive!");
|
||||
}
|
||||
}
|
||||
|
||||
void Sampler::divide_samples(size_t target_sample_count, const std::vector<std::pair<int32_t, int32_t>> &samples)
|
||||
{
|
||||
// Initialize indicies lookup and make 1 split with entire sample range.
|
||||
indicies.resize(samples.size());
|
||||
for (size_t i = 0; i < samples.size(); ++i) {
|
||||
indicies[i] = i;
|
||||
}
|
||||
|
||||
splits.reserve(2);
|
||||
splits.push_back(0);
|
||||
splits.push_back(samples.size());
|
||||
|
||||
size_t divisions = std::ceil(std::sqrt(target_sample_count) / 2.);
|
||||
if (divisions == 0) {
|
||||
throw std::runtime_error("Math failure, unreachable!");
|
||||
}
|
||||
|
||||
if (divisions > samples.size()) {
|
||||
// Handle cases where there are few samples.
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursively split samples first 50% / 50% in x direction, and then
|
||||
// 50% / 50% in y direction. Repeat until the bucket is smaller than
|
||||
// kMinSplit or the samples have been divided `divisions` times.
|
||||
std::vector<size_t> new_splits;
|
||||
for (size_t division_count = 0; division_count < divisions; ++division_count) {
|
||||
new_splits.clear();
|
||||
new_splits.push_back(0);
|
||||
for (size_t i = 0; i < splits.size() - 1; ++i) {
|
||||
size_t split_begin = splits.at(i);
|
||||
size_t split_end = splits.at(i + 1);
|
||||
if (split_end > indicies.size()) {
|
||||
throw std::runtime_error("split_end is not valid!");
|
||||
}
|
||||
if (split_begin >= split_end) {
|
||||
throw std::runtime_error("Invalid split from earlier pass!");
|
||||
}
|
||||
|
||||
std::vector<size_t>::iterator begin = indicies.begin() + split_begin;
|
||||
std::vector<size_t>::iterator end = indicies.begin() + split_end;
|
||||
|
||||
if (std::distance(begin, end) < kMinSplit) {
|
||||
add_split(&new_splits, split_begin);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to split samples 50/50 in x direction.
|
||||
size_t split = partition_x(begin, end, samples);
|
||||
// Try to split samples 50/50 in y direction after the x split.
|
||||
size_t split_y1 = partition_y(begin, begin + split, samples);
|
||||
size_t split_y2 = partition_y(begin + split, end, samples);
|
||||
|
||||
// Because the y2 split starts at split, add it here.
|
||||
split_y2 += split;
|
||||
|
||||
add_split(&new_splits, split_begin);
|
||||
add_split(&new_splits, split_begin + split_y1);
|
||||
add_split(&new_splits, split_begin + split);
|
||||
add_split(&new_splits, split_begin + split_y2);
|
||||
}
|
||||
|
||||
add_split(&new_splits, samples.size());
|
||||
|
||||
if (new_splits.front() != 0) {
|
||||
throw std::runtime_error("Split must start at 0");
|
||||
}
|
||||
if (new_splits.back() != samples.size()) {
|
||||
throw std::runtime_error("Split must end at last element");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < new_splits.size() - 1; ++i) {
|
||||
if (new_splits[i] >= new_splits[i + 1]) {
|
||||
throw std::runtime_error("Split indicies must be increasing");
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(splits, new_splits);
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
69
fpga_interchange/sampler.h
Normal file
69
fpga_interchange/sampler.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 SAMPLER_H_
|
||||
#define SAMPLER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "nextpnr_namespaces.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// Given a set of coordinates, generates random samples that are geometric
|
||||
// distributed.
|
||||
struct Sampler
|
||||
{
|
||||
|
||||
void divide_samples(size_t target_sample_count, const std::vector<std::pair<int32_t, int32_t>> &samples);
|
||||
|
||||
size_t number_of_regions() const { return splits.size() - 1; }
|
||||
|
||||
size_t get_sample_from_region(size_t region, std::function<int32_t()> rng) const
|
||||
{
|
||||
if (region >= (splits.size() - 1)) {
|
||||
throw std::runtime_error("region out of range");
|
||||
}
|
||||
size_t split_begin = splits[region];
|
||||
size_t split_end = splits[region + 1];
|
||||
if (split_begin == split_end) {
|
||||
throw std::runtime_error("Splits should never be empty!");
|
||||
}
|
||||
|
||||
// Pick a random element from that region.
|
||||
return indicies[split_begin + (rng() % (split_end - split_begin))];
|
||||
}
|
||||
|
||||
size_t get_sample(std::function<int32_t()> rng) const
|
||||
{
|
||||
size_t region = rng() % number_of_regions();
|
||||
return get_sample_from_region(region, rng);
|
||||
}
|
||||
|
||||
std::vector<size_t> indicies;
|
||||
std::vector<size_t> splits;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif /* SAMPLER_H_ */
|
83
fpga_interchange/type_wire.cc
Normal file
83
fpga_interchange/type_wire.cc
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 "type_wire.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
TypeWireId::TypeWireId(const Context *ctx, WireId wire_inst)
|
||||
{
|
||||
NPNR_ASSERT(wire_inst != WireId());
|
||||
|
||||
if (wire_inst.tile == -1) {
|
||||
auto &tile_wire = ctx->chip_info->nodes[wire_inst.index].tile_wires[0];
|
||||
type = ctx->chip_info->tiles[tile_wire.tile].type;
|
||||
index = tile_wire.index;
|
||||
} else {
|
||||
type = ctx->chip_info->tiles[wire_inst.tile].type;
|
||||
index = wire_inst.index;
|
||||
}
|
||||
}
|
||||
|
||||
TypeWireSet::TypeWireSet(const Context *ctx, WireId wire)
|
||||
{
|
||||
if (wire.tile == -1) {
|
||||
const auto &node_data = ctx->chip_info->nodes[wire.index];
|
||||
wire_types_.reserve(node_data.tile_wires.size());
|
||||
for (const auto &tile_wire : node_data.tile_wires) {
|
||||
wire_types_.emplace_back();
|
||||
wire_types_.back().type = ctx->chip_info->tiles[tile_wire.tile].type;
|
||||
wire_types_.back().index = tile_wire.index;
|
||||
}
|
||||
} else {
|
||||
TypeWireId wire_type(ctx, wire);
|
||||
wire_types_.push_back(wire_type);
|
||||
}
|
||||
|
||||
std::sort(wire_types_.begin(), wire_types_.end());
|
||||
|
||||
hash_ = 0;
|
||||
boost::hash_combine(hash_, std::hash<size_t>()(wire_types_.size()));
|
||||
for (const auto &wire : wire_types_) {
|
||||
boost::hash_combine(hash_, std::hash<NEXTPNR_NAMESPACE_PREFIX TypeWireId>()(wire));
|
||||
}
|
||||
}
|
||||
|
||||
TypeWireId::TypeWireId(lookahead_storage::TypeWireId::Reader reader) : type(reader.getType()), index(reader.getIndex())
|
||||
{
|
||||
}
|
||||
void TypeWireId::to_builder(lookahead_storage::TypeWireId::Builder builder) const
|
||||
{
|
||||
builder.setType(type);
|
||||
builder.setIndex(index);
|
||||
}
|
||||
|
||||
TypeWirePair::TypeWirePair(lookahead_storage::TypeWirePair::Reader reader) : src(reader.getSrc()), dst(reader.getDst())
|
||||
{
|
||||
}
|
||||
|
||||
void TypeWirePair::to_builder(lookahead_storage::TypeWirePair::Builder builder) const
|
||||
{
|
||||
src.to_builder(builder.getSrc());
|
||||
dst.to_builder(builder.getDst());
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
111
fpga_interchange/type_wire.h
Normal file
111
fpga_interchange/type_wire.h
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 TYPE_WIRE_H
|
||||
#define TYPE_WIRE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "nextpnr_namespaces.h"
|
||||
#include "nextpnr_types.h"
|
||||
|
||||
#include "lookahead.capnp.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct Context;
|
||||
|
||||
struct TypeWireId
|
||||
{
|
||||
TypeWireId() : type(-1), index(-1) {}
|
||||
TypeWireId(const Context *ctx, WireId wire_inst);
|
||||
|
||||
explicit TypeWireId(lookahead_storage::TypeWireId::Reader reader);
|
||||
void to_builder(lookahead_storage::TypeWireId::Builder builder) const;
|
||||
|
||||
bool operator==(const TypeWireId &other) const { return type == other.type && index == other.index; }
|
||||
bool operator!=(const TypeWireId &other) const { return type != other.type || index != other.index; }
|
||||
bool operator<(const TypeWireId &other) const
|
||||
{
|
||||
return type < other.type || (type == other.type && index < other.index);
|
||||
}
|
||||
|
||||
int32_t type;
|
||||
int32_t index;
|
||||
};
|
||||
|
||||
struct TypeWirePair
|
||||
{
|
||||
TypeWireId src;
|
||||
TypeWireId dst;
|
||||
|
||||
TypeWirePair() = default;
|
||||
explicit TypeWirePair(lookahead_storage::TypeWirePair::Reader reader);
|
||||
void to_builder(lookahead_storage::TypeWirePair::Builder builder) const;
|
||||
|
||||
bool operator==(const TypeWirePair &other) const { return src == other.src && dst == other.dst; }
|
||||
bool operator!=(const TypeWirePair &other) const { return src != other.src || dst != other.dst; }
|
||||
};
|
||||
|
||||
struct TypeWireSet
|
||||
{
|
||||
public:
|
||||
TypeWireSet(const Context *ctx, WireId wire);
|
||||
std::size_t hash() const { return hash_; }
|
||||
|
||||
bool operator==(const TypeWireSet &other) const { return wire_types_ == other.wire_types_; }
|
||||
bool operator!=(const TypeWireSet &other) const { return wire_types_ != other.wire_types_; }
|
||||
|
||||
private:
|
||||
std::size_t hash_;
|
||||
std::vector<TypeWireId> wire_types_;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX TypeWireId>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX TypeWireId &wire) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, std::hash<int>()(wire.type));
|
||||
boost::hash_combine(seed, std::hash<int>()(wire.index));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX TypeWirePair>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX TypeWirePair &pair) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX TypeWireId>()(pair.src));
|
||||
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX TypeWireId>()(pair.dst));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX TypeWireSet>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX TypeWireSet &set) const noexcept { return set.hash(); }
|
||||
};
|
||||
|
||||
#endif /* TYPE_WIRE_H */
|
Loading…
Reference in New Issue
Block a user