Merge pull request #219 from daveshah1/placer_heap

HeAP-based analytical placer
This commit is contained in:
David Shah 2019-03-25 16:24:02 +00:00 committed by GitHub
commit c67b8259bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2573 additions and 223 deletions

View File

@ -8,7 +8,7 @@ RUN set -e -x ;\
apt-get -y install \
build-essential autoconf cmake clang bison wget flex gperf \
libreadline-dev gawk tcl-dev libffi-dev graphviz xdot python3-dev \
libboost-all-dev qt5-default git libftdi-dev pkg-config
libboost-all-dev qt5-default git libftdi-dev pkg-config libeigen3-dev
RUN set -e -x ;\
mkdir -p /usr/local/src ;\

View File

@ -5,6 +5,8 @@ project(nextpnr)
option(BUILD_GUI "Build GUI" ON)
option(BUILD_PYTHON "Build Python Integration" ON)
option(BUILD_TESTS "Build GUI" OFF)
option(BUILD_HEAP "Build HeAP analytic placer" ON)
option(USE_OPENMP "Use OpenMP to accelerate analytic placer" OFF)
option(COVERAGE "Add code coverage info" OFF)
option(STATIC_BUILD "Create static build" OFF)
option(EXTERNAL_CHIPDB "Create build with pre-built chipdb binaries" OFF)
@ -53,12 +55,16 @@ endforeach()
set(CMAKE_CXX_STANDARD 11)
if (MSVC)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996 /wd4127")
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996 /wd4127")
else()
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -fPIC -ggdb -pipe")
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g -pipe")
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -fPIC -ggdb -pipe")
if (USE_OPENMP)
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g -pipe -fopenmp")
else()
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -fPIC -O3 -g -pipe")
endif()
endif()
set(CMAKE_DEFIN)
@ -181,6 +187,14 @@ if (BUILD_PYTHON)
endif()
include_directories(common/ json/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
if(BUILD_HEAP)
find_package (Eigen3 REQUIRED NO_MODULE)
include_directories(${EIGEN3_INCLUDE_DIRS})
add_definitions(${EIGEN3_DEFINITIONS})
add_definitions(-DWITH_HEAP)
endif()
aux_source_directory(common/ COMMON_SRC_FILES)
aux_source_directory(json/ JSON_PARSER_FILES)
set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES})

View File

@ -36,6 +36,7 @@ of the selected architecture:
- Python 3.5 or later, including development libraries (`python3-dev` for Ubuntu)
- on Windows make sure to install same version as supported by [vcpkg](https://github.com/Microsoft/vcpkg/blob/master/ports/python3/CONTROL)
- Boost libraries (`libboost-dev libboost-filesystem-dev libboost-thread-dev libboost-program-options-dev libboost-python-dev libboost-dev` or `libboost-all-dev` for Ubuntu)
- Eigen3 (`libeigen3-dev` for Ubuntu) is required to build the analytic placer
- Latest git Yosys is required to synthesise the demo design
- For building on Windows with MSVC, usage of vcpkg is advised for dependency installation.
- For 32 bit builds: `vcpkg install boost-filesystem boost-program-options boost-thread boost-python qt5-base`
@ -119,11 +120,11 @@ Use cmake `-D` options to specify which version of nextpnr you want to build.
Use `-DARCH=...` to set the architecture. It is a semicolon separated list.
Use `cmake . -DARCH=all` to build all supported architectures.
The following runs a debug build of the iCE40 architecture without GUI
and without Python support and only HX1K support:
The following runs a debug build of the iCE40 architecture without GUI,
without Python support, without the HeAP analytic placer and only HX1K support:
```
cmake -DARCH=ice40 -DCMAKE_BUILD_TYPE=Debug -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF -DICE40_HX1K_ONLY=1 .
cmake -DARCH=ice40 -DCMAKE_BUILD_TYPE=Debug -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF -DBUILD_HEAP=OFF -DICE40_HX1K_ONLY=1 .
make -j$(nproc)
```
@ -134,6 +135,9 @@ cmake -DARCH=ice40 -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF -DSTATIC_BUILD=ON .
make -j$(nproc)
```
The HeAP placer's solver can optionally use OpenMP for a speedup on very large designs. Enable this by passing
`-DUSE_OPENMP=yes` to cmake (compiler support may vary).
You can change the location where nextpnr will be installed (this will usually default to `/usr/local`) by using
`-DCMAKE_INSTALL_PREFIX=/install/prefix`.

View File

@ -27,6 +27,7 @@
#include "pybindings.h"
#endif
#include <boost/algorithm/string/join.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/program_options.hpp>
#include <fstream>
@ -120,8 +121,18 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
general.add_options()("seed", po::value<int>(), "seed value for random number generator");
general.add_options()("randomize-seed,r", "randomize seed value for random number generator");
general.add_options()(
"placer", po::value<std::string>(),
std::string("placer algorithm to use; available: " + boost::algorithm::join(Arch::availablePlacers, ", ") +
"; default: " + Arch::defaultPlacer)
.c_str());
general.add_options()("slack_redist_iter", po::value<int>(), "number of iterations between slack redistribution");
general.add_options()("cstrweight", po::value<float>(), "placer weighting for relative constraint satisfaction");
general.add_options()("starttemp", po::value<float>(), "placer SA start temperature");
general.add_options()("placer-budgets", "use budget rather than criticality in placer timing weights");
general.add_options()("pack-only", "pack design only without placement or routing");
general.add_options()("ignore-loops", "ignore combinational loops in timing analysis");
@ -183,10 +194,27 @@ void CommandHandler::setupContext(Context *ctx)
settings->set("timing/allowFail", true);
}
if (vm.count("placer")) {
std::string placer = vm["placer"].as<std::string>();
if (std::find(Arch::availablePlacers.begin(), Arch::availablePlacers.end(), placer) ==
Arch::availablePlacers.end())
log_error("Placer algorithm '%s' is not supported (available options: %s)\n", placer.c_str(),
boost::algorithm::join(Arch::availablePlacers, ", ").c_str());
settings->set("placer", placer);
} else {
settings->set("placer", Arch::defaultPlacer);
}
if (vm.count("cstrweight")) {
settings->set("placer1/constraintWeight", vm["cstrweight"].as<float>());
}
if (vm.count("starttemp")) {
settings->set("placer1/startTemp", vm["starttemp"].as<float>());
}
if (vm.count("placer-budgets")) {
settings->set("placer1/budgetBased", true);
}
if (vm.count("freq")) {
auto freq = vm["freq"].as<double>();
if (freq > 0)

View File

@ -221,6 +221,9 @@ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &us
return 0;
#endif
if (net_info->wires.empty())
return predictDelay(net_info, user_info);
WireId src_wire = getNetinfoSourceWire(net_info);
if (src_wire == WireId())
return 0;
@ -421,4 +424,25 @@ void BaseCtx::addClock(IdString net, float freq)
}
}
void BaseCtx::createRectangularRegion(IdString name, int x0, int y0, int x1, int y1)
{
std::unique_ptr<Region> new_region(new Region());
new_region->name = name;
new_region->constr_bels = true;
new_region->constr_pips = false;
new_region->constr_wires = false;
for (int x = x0; x <= x1; x++) {
for (int y = y0; y <= y1; y++) {
for (auto bel : getCtx()->getBelsByTile(x, y))
new_region->bels.insert(bel);
}
}
region[name] = std::move(new_region);
}
void BaseCtx::addBelToRegion(IdString name, BelId bel) { region[name]->bels.insert(bel); }
void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name)
{
cells[cell]->region = region[region_name].get();
}
NEXTPNR_NAMESPACE_END

View File

@ -637,6 +637,9 @@ struct BaseCtx
// Intended to simplify Python API
void addClock(IdString net, float freq);
void createRectangularRegion(IdString name, int x0, int y0, int x1, int y1);
void addBelToRegion(IdString name, BelId bel);
void constrainCellToRegion(IdString cell, IdString region_name);
};
NEXTPNR_NAMESPACE_END

View File

@ -304,7 +304,7 @@ class ConstraintLegaliseWorker
// Set the strength to locked on all cells in chain
void lockdown_chain(CellInfo *root)
{
root->belStrength = STRENGTH_LOCKED;
root->belStrength = STRENGTH_STRONG;
for (auto child : root->constr_children)
lockdown_chain(child);
}
@ -380,7 +380,7 @@ class ConstraintLegaliseWorker
rippedCells.insert(confl_cell->name);
}
}
ctx->bindBel(target, ctx->cells.at(cp.first).get(), STRENGTH_LOCKED);
ctx->bindBel(target, ctx->cells.at(cp.first).get(), STRENGTH_STRONG);
rippedCells.erase(cp.first);
}
for (auto cp : solution) {
@ -529,4 +529,12 @@ int get_constraints_distance(const Context *ctx, const CellInfo *cell)
return dist;
}
bool check_cell_bel_region(const CellInfo *cell, BelId bel)
{
if (cell->region != nullptr && cell->region->constr_bels && !cell->region->bels.count(bel))
return false;
else
return true;
}
NEXTPNR_NAMESPACE_END

View File

@ -49,6 +49,10 @@ bool legalise_relative_constraints(Context *ctx);
// Get the total distance from satisfied constraints for a cell
int get_constraints_distance(const Context *ctx, const CellInfo *cell);
// Check that a Bel is within the region for a cell
bool check_cell_bel_region(const CellInfo *cell, BelId bel);
NEXTPNR_NAMESPACE_END
#endif

File diff suppressed because it is too large Load Diff

View File

@ -29,9 +29,13 @@ struct Placer1Cfg : public Settings
Placer1Cfg(Context *ctx);
float constraintWeight;
int minBelsForGridPick;
bool budgetBased;
float startTemp;
int timingFanoutThresh;
};
extern bool placer1(Context *ctx, Placer1Cfg cfg);
extern bool placer1_refine(Context *ctx, Placer1Cfg cfg);
NEXTPNR_NAMESPACE_END

1544
common/placer_heap.cc Normal file

File diff suppressed because it is too large Load Diff

47
common/placer_heap.h Normal file
View File

@ -0,0 +1,47 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2019 David Shah <david@symbioticeda.com>
*
* 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.
*
* [[cite]] HeAP
* Analytical Placement for Heterogeneous FPGAs, Marcel Gort and Jason H. Anderson
* https://janders.eecg.utoronto.ca/pdfs/marcelfpl12.pdf
*
* [[cite]] SimPL
* SimPL: An Effective Placement Algorithm, Myung-Chul Kim, Dong-Jin Lee and Igor L. Markov
* http://www.ece.umich.edu/cse/awards/pdfs/iccad10-simpl.pdf
*/
#ifndef PLACER_HEAP_H
#define PLACER_HEAP
#include "nextpnr.h"
#include "settings.h"
NEXTPNR_NAMESPACE_BEGIN
struct PlacerHeapCfg : public Settings
{
PlacerHeapCfg(Context *ctx);
float alpha;
float criticalityExponent;
float timingWeight;
std::unordered_set<IdString> ioBufTypes;
};
extern bool placer_heap(Context *ctx, PlacerHeapCfg cfg);
NEXTPNR_NAMESPACE_END
#endif

View File

@ -104,6 +104,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
typedef std::unordered_map<IdString, std::string> AttrMap;
typedef std::unordered_map<IdString, PortInfo> PortMap;
typedef std::unordered_map<IdString, IdString> PinMap;
typedef std::unordered_map<IdString, std::unique_ptr<Region>> RegionMap;
class_<BaseCtx, BaseCtx *, boost::noncopyable>("BaseCtx", no_init);
@ -135,6 +136,8 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
typedef std::vector<PortRef> PortRefVector;
typedef std::unordered_map<WireId, PipMap> WireMap;
typedef std::unordered_set<BelId> BelSet;
typedef std::unordered_set<WireId> WireSet;
auto ni_cls = class_<ContextualWrapper<NetInfo &>>("NetInfo", no_init);
readwrite_wrapper<NetInfo &, decltype(&NetInfo::name), &NetInfo::name, conv_to_str<IdString>,
@ -163,10 +166,25 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
def("parse_json", parse_json_shim);
def("load_design", load_design_shim, return_value_policy<manage_new_object>());
auto region_cls = class_<ContextualWrapper<Region &>>("Region", no_init);
readwrite_wrapper<Region &, decltype(&Region::name), &Region::name, conv_to_str<IdString>,
conv_from_str<IdString>>::def_wrap(region_cls, "name");
readwrite_wrapper<Region &, decltype(&Region::constr_bels), &Region::constr_bels, pass_through<bool>,
pass_through<bool>>::def_wrap(region_cls, "constr_bels");
readwrite_wrapper<Region &, decltype(&Region::constr_wires), &Region::constr_wires, pass_through<bool>,
pass_through<bool>>::def_wrap(region_cls, "constr_bels");
readwrite_wrapper<Region &, decltype(&Region::constr_pips), &Region::constr_pips, pass_through<bool>,
pass_through<bool>>::def_wrap(region_cls, "constr_pips");
readonly_wrapper<Region &, decltype(&Region::bels), &Region::bels, wrap_context<BelSet &>>::def_wrap(region_cls,
"bels");
readonly_wrapper<Region &, decltype(&Region::wires), &Region::wires, wrap_context<WireSet &>>::def_wrap(region_cls,
"wires");
WRAP_MAP(AttrMap, pass_through<std::string>, "AttrMap");
WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap");
WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap");
WRAP_MAP(WireMap, wrap_context<PipMap &>, "WireMap");
WRAP_MAP_UPTR(RegionMap, "RegionMap");
WRAP_VECTOR(PortRefVector, wrap_context<PortRef &>);

View File

@ -345,6 +345,12 @@ template <typename T, typename value_conv> struct map_wrapper
std::terminate();
}
static bool contains(wrapped_map &x, std::string const &i)
{
K k = PythonConversion::string_converter<K>().from_str(x.ctx, i);
return x.base.count(k);
}
static void wrap(const char *map_name, const char *kv_name, const char *kv_iter_name, const char *iter_name)
{
map_pair_wrapper<typename KV::first_type, typename KV::second_type, value_conv>::wrap(kv_name, kv_iter_name);
@ -353,6 +359,7 @@ template <typename T, typename value_conv> struct map_wrapper
class_<wrapped_map>(map_name, no_init)
.def("__iter__", rw::iter)
.def("__len__", len)
.def("__contains__", contains)
.def("__getitem__", get)
.def("__setitem__", set, with_custodian_and_ward<1, 2>());
}
@ -465,6 +472,12 @@ template <typename T> struct map_wrapper_uptr
std::terminate();
}
static bool contains(wrapped_map &x, std::string const &i)
{
K k = PythonConversion::string_converter<K>().from_str(x.ctx, i);
return x.base.count(k);
}
static void wrap(const char *map_name, const char *kv_name, const char *kv_iter_name, const char *iter_name)
{
map_pair_wrapper_uptr<typename KV::first_type, typename KV::second_type>::wrap(kv_name, kv_iter_name);
@ -473,6 +486,7 @@ template <typename T> struct map_wrapper_uptr
class_<wrapped_map>(map_name, no_init)
.def("__iter__", rw::iter)
.def("__len__", len)
.def("__contains__", contains)
.def("__getitem__", get)
.def("__setitem__", set, with_custodian_and_ward<1, 2>());
}

View File

@ -269,7 +269,7 @@ template <typename Class, typename FuncT, FuncT fn, typename arg1_conv, typename
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
};
// Three parameters, one return
// Three parameters, no return
template <typename Class, typename FuncT, FuncT fn, typename arg1_conv, typename arg2_conv, typename arg3_conv>
struct fn_wrapper_3a_v
{
@ -288,6 +288,30 @@ struct fn_wrapper_3a_v
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
};
// Five parameters, no return
template <typename Class, typename FuncT, FuncT fn, typename arg1_conv, typename arg2_conv, typename arg3_conv,
typename arg4_conv, typename arg5_conv>
struct fn_wrapper_5a_v
{
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
using conv_arg1_type = typename arg1_conv::arg_type;
using conv_arg2_type = typename arg2_conv::arg_type;
using conv_arg3_type = typename arg3_conv::arg_type;
using conv_arg4_type = typename arg4_conv::arg_type;
using conv_arg5_type = typename arg5_conv::arg_type;
static void wrapped_fn(class_type &cls, conv_arg1_type arg1, conv_arg2_type arg2, conv_arg3_type arg3,
conv_arg4_type arg4, conv_arg5_type arg5)
{
Context *ctx = get_ctx<Class>(cls);
Class &base = get_base<Class>(cls);
return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3),
arg4_conv()(ctx, arg4), arg5_conv()(ctx, arg5));
}
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
};
// Wrapped getter
template <typename Class, typename MemT, MemT mem, typename v_conv> struct readonly_wrapper
{

View File

@ -45,19 +45,30 @@ class Settings
return defaultValue;
}
template <typename T> void set(const char *name, T value)
{
IdString id = ctx->id(name);
auto pair = ctx->settings.emplace(id, std::to_string(value));
if (!pair.second) {
ctx->settings[pair.first->first] = value;
}
}
template <typename T> void set(const char *name, T value);
private:
Context *ctx;
};
template <typename T> inline void Settings::set(const char *name, T value)
{
IdString id = ctx->id(name);
auto pair = ctx->settings.emplace(id, std::to_string(value));
if (!pair.second) {
ctx->settings[pair.first->first] = value;
}
}
template <> inline void Settings::set<std::string>(const char *name, std::string value)
{
IdString id = ctx->id(name);
auto pair = ctx->settings.emplace(id, value);
if (!pair.second) {
ctx->settings[pair.first->first] = value;
}
}
NEXTPNR_NAMESPACE_END
#endif // SETTINGS_H

View File

@ -904,10 +904,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
if (!warn_on_failure || passed)
log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
else
if (bool_or_default(ctx->settings, ctx->id("timing/allowFail"), false))
else if (bool_or_default(ctx->settings, ctx->id("timing/allowFail"), false))
log_warning("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
else
log_nonfatal_error("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);

View File

@ -490,3 +490,14 @@ a certain number of different clock signals allowed for a group of bels.
Returns true if a bell in the current configuration is valid, i.e. if
`isValidBelForCell()` would return true for the current mapping.
### static const std::string defaultPlacer
Name of the default placement algorithm for the architecture, if
`--placer` isn't specified on the command line.
### static const std::vector\<std::string\> availablePlacers
Name of available placer algorithms for the architecture, used
to provide help for and validate `--placer`.

View File

@ -28,6 +28,7 @@
#include "log.h"
#include "nextpnr.h"
#include "placer1.h"
#include "placer_heap.h"
#include "router1.h"
#include "timing.h"
#include "util.h"
@ -456,6 +457,7 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
auto src_loc = est_location(src), dst_loc = est_location(dst);
int dx = abs(src_loc.first - dst_loc.first), dy = abs(src_loc.second - dst_loc.second);
return (130 - 25 * args.speed) *
(6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
}
@ -467,7 +469,6 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
return 0;
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
// Encourage use of direct interconnect
if (driver_loc.x == sink_loc.x && driver_loc.y == sink_loc.y) {
if ((sink.port == id_A0 || sink.port == id_A1) && (driver.port == id_F1) &&
@ -485,6 +486,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
}
int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y);
return (130 - 25 * args.speed) *
(6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
}
@ -506,10 +508,23 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
bool Arch::place()
{
bool result = placer1(getCtx(), Placer1Cfg(getCtx()));
if (result)
permute_luts();
return result;
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
if (placer == "heap") {
PlacerHeapCfg cfg(getCtx());
cfg.criticalityExponent = 7;
cfg.ioBufTypes.insert(id_TRELLIS_IO);
if (!placer_heap(getCtx(), cfg))
return false;
} else if (placer == "sa") {
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
return false;
} else {
log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str());
}
permute_luts();
return true;
}
bool Arch::route()
@ -605,6 +620,11 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
bool Arch::getDelayFromTimingDatabase(IdString tctype, IdString from, IdString to, DelayInfo &delay) const
{
auto fnd_dk = celldelay_cache.find({tctype, from, to});
if (fnd_dk != celldelay_cache.end()) {
delay = fnd_dk->second.second;
return fnd_dk->second.first;
}
for (int i = 0; i < speed_grade->num_cell_timings; i++) {
const auto &tc = speed_grade->cell_timings[i];
if (tc.cell_type == tctype.index) {
@ -613,9 +633,11 @@ bool Arch::getDelayFromTimingDatabase(IdString tctype, IdString from, IdString t
if (dly.from_port == from.index && dly.to_port == to.index) {
delay.max_delay = dly.max_delay;
delay.min_delay = dly.min_delay;
celldelay_cache[{tctype, from, to}] = std::make_pair(true, delay);
return true;
}
}
celldelay_cache[{tctype, from, to}] = std::make_pair(false, DelayInfo());
return false;
}
}
@ -645,7 +667,6 @@ void Arch::getSetupHoldFromTimingDatabase(IdString tctype, IdString clock, IdStr
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{
// Data for -8 grade
if (cell->type == id_TRELLIS_SLICE) {
bool has_carry = cell->sliceInfo.is_carry;
@ -965,4 +986,16 @@ WireId Arch::getBankECLK(int bank, int eclk)
return getWireByLocAndBasename(Location(0, 0), "G_BANK" + std::to_string(bank) + "ECLK" + std::to_string(eclk));
}
#ifdef WITH_HEAP
const std::string Arch::defaultPlacer = "heap";
#else
const std::string Arch::defaultPlacer = "sa";
#endif
const std::vector<std::string> Arch::availablePlacers = {"sa",
#ifdef WITH_HEAP
"heap"
#endif
};
NEXTPNR_NAMESPACE_END

View File

@ -448,6 +448,30 @@ struct ArchArgs
} speed = SPEED_6;
};
struct DelayKey
{
IdString celltype, from, to;
inline bool operator==(const DelayKey &other) const
{
return celltype == other.celltype && from == other.from && to == other.to;
}
};
NEXTPNR_NAMESPACE_END
namespace std {
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DelayKey>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DelayKey &dk) const noexcept
{
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(dk.celltype);
seed ^= std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(dk.from) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(dk.to) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
} // namespace std
NEXTPNR_NAMESPACE_BEGIN
struct Arch : BaseCtx
{
const ChipInfoPOD *chip_info;
@ -1019,6 +1043,11 @@ struct Arch : BaseCtx
IdString id_clk, id_lsr;
IdString id_clkmux, id_lsrmux;
IdString id_srmode, id_mode;
mutable std::unordered_map<DelayKey, std::pair<bool, DelayInfo>> celldelay_cache;
static const std::string defaultPlacer;
static const std::vector<std::string> availablePlacers;
};
NEXTPNR_NAMESPACE_END

View File

@ -133,6 +133,13 @@ void arch_wrap_python()
fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
pass_through<float>>::def_wrap(ctx_cls, "addClock");
fn_wrapper_5a_v<Context, decltype(&Context::createRectangularRegion), &Context::createRectangularRegion,
conv_from_str<IdString>, pass_through<int>, pass_through<int>, pass_through<int>,
pass_through<int>>::def_wrap(ctx_cls, "createRectangularRegion");
fn_wrapper_2a_v<Context, decltype(&Context::addBelToRegion), &Context::addBelToRegion, conv_from_str<IdString>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "addBelToRegion");
fn_wrapper_2a_v<Context, decltype(&Context::constrainCellToRegion), &Context::constrainCellToRegion,
conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "constrainCellToRegion");
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);

View File

@ -149,8 +149,8 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
chipArgs.speed = ArchArgs::SPEED_6;
}
}
return std::unique_ptr<Context>(new Context(chipArgs));
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
return ctx;
}
void ECP5CommandHandler::customAfterLoad(Context *ctx)

View File

@ -21,6 +21,7 @@
#include "nextpnr.h"
#include "placer1.h"
#include "router1.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
@ -439,7 +440,16 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
// ---------------------------------------------------------------
bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
bool Arch::place()
{
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
// FIXME: No HeAP because it needs a list of IO buffers
if (placer == "sa") {
return placer1(getCtx(), Placer1Cfg(getCtx()));
} else {
log_error("Generic architecture does not support placer '%s'\n", placer.c_str());
}
}
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
@ -476,4 +486,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; }
bool Arch::isBelLocationValid(BelId bel) const { return true; }
const std::string Arch::defaultPlacer = "sa";
const std::vector<std::string> Arch::availablePlacers = {"sa"};
NEXTPNR_NAMESPACE_END

View File

@ -240,6 +240,9 @@ struct Arch : BaseCtx
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
bool isBelLocationValid(BelId bel) const;
static const std::string defaultPlacer;
static const std::vector<std::string> availablePlacers;
};
NEXTPNR_NAMESPACE_END

View File

@ -645,10 +645,10 @@ void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
return;
bool shift = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
bool ctrl = QApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
bool btn_right = event->buttons() & Qt::RightButton;
bool btn_mid = event->buttons() & Qt::MidButton;
bool btn_left = event->buttons() & Qt::LeftButton;
bool btn_mid = event->buttons() & Qt::MidButton;
bool btn_left = event->buttons() & Qt::LeftButton;
if (btn_right || btn_mid || (btn_left && shift)) {
lastDragPos_ = event->pos();
@ -687,8 +687,8 @@ void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
bool shift = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool btn_right = event->buttons() & Qt::RightButton;
bool btn_mid = event->buttons() & Qt::MidButton;
bool btn_left = event->buttons() & Qt::LeftButton;
bool btn_mid = event->buttons() & Qt::MidButton;
bool btn_left = event->buttons() & Qt::LeftButton;
if (btn_right || btn_mid || (btn_left && shift)) {
const int dx = event->x() - lastDragPos_.x();

View File

@ -26,10 +26,10 @@
#include "log.h"
#include "nextpnr.h"
#include "placer1.h"
#include "placer_heap.h"
#include "router1.h"
#include "timing_opt.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
@ -671,8 +671,18 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
bool Arch::place()
{
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
return false;
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
if (placer == "heap") {
PlacerHeapCfg cfg(getCtx());
cfg.ioBufTypes.insert(id_SB_IO);
if (!placer_heap(getCtx(), cfg))
return false;
} else if (placer == "sa") {
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
return false;
} else {
log_error("iCE40 architecture does not support placer '%s'\n", placer.c_str());
}
if (bool_or_default(settings, id("opt_timing"), false)) {
TimingOptCfg tocfg(getCtx());
tocfg.cellTypes.insert(id_ICESTORM_LC);
@ -1198,4 +1208,12 @@ void Arch::assignCellInfo(CellInfo *cell)
}
}
const std::string Arch::defaultPlacer = "sa";
const std::vector<std::string> Arch::availablePlacers = {"sa",
#ifdef WITH_HEAP
"heap"
#endif
};
NEXTPNR_NAMESPACE_END

View File

@ -897,6 +897,9 @@ struct Arch : BaseCtx
IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT));
return std::stoi(std::string("") + glb_net.str(this).back());
}
static const std::string defaultPlacer;
static const std::vector<std::string> availablePlacers;
};
void ice40DelayFuzzerMain(Context *ctx);

View File

@ -144,6 +144,13 @@ void arch_wrap_python()
fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
pass_through<float>>::def_wrap(ctx_cls, "addClock");
fn_wrapper_5a_v<Context, decltype(&Context::createRectangularRegion), &Context::createRectangularRegion,
conv_from_str<IdString>, pass_through<int>, pass_through<int>, pass_through<int>,
pass_through<int>>::def_wrap(ctx_cls, "createRectangularRegion");
fn_wrapper_2a_v<Context, decltype(&Context::addBelToRegion), &Context::addBelToRegion, conv_from_str<IdString>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "addBelToRegion");
fn_wrapper_2a_v<Context, decltype(&Context::constrainCellToRegion), &Context::constrainCellToRegion,
conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "constrainCellToRegion");
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);

4
ice40/examples/floorplan/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.json
*.asc
*.bin
__pycache__

View File

@ -0,0 +1,5 @@
ctx.createRectangularRegion("osc", 1, 1, 1, 4)
for cell, cellinfo in ctx.cells:
if "ringosc" in cellinfo.attrs:
print("Floorplanned cell %s" % cell)
ctx.constrainCellToRegion(cell, "osc")

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -ex
yosys -p "synth_ice40 -top top -json floorplan.json" floorplan.v
../../../nextpnr-ice40 --up5k --json floorplan.json --pcf icebreaker.pcf --asc floorplan.asc --ignore-loops --pre-place floorplan.py
icepack floorplan.asc floorplan.bin
iceprog floorplan.bin

View File

@ -0,0 +1,22 @@
module top(output LED1, LED2, LED3, LED4, LED5);
localparam N = 31;
wire [N:0] x;
assign x[0] = x[N];
genvar ii;
generate
for (ii = 0; ii < N; ii = ii + 1) begin
(* ringosc *)
SB_LUT4 #(.LUT_INIT(1)) lut_i(.I0(x[ii]), .I1(), .I2(), .I3(), .O(x[ii+1]));
end
endgenerate
assign clk = x[N];
reg [19:0] ctr;
always @(posedge clk)
ctr <= ctr + 1'b1;
assign {LED5, LED4, LED3, LED2, LED1} = ctr[19:15];
endmodule

View File

@ -0,0 +1,5 @@
set_io -nowarn LED1 26
set_io -nowarn LED2 27
set_io -nowarn LED3 25
set_io -nowarn LED4 23
set_io -nowarn LED5 21

View File

@ -176,7 +176,6 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext()
ctx->settings[ctx->id("opt_timing")] = "1";
if (vm.count("pcf-allow-unconstrained"))
ctx->settings[ctx->id("pcf_allow_unconstrained")] = "1";
return ctx;
}

2
tests

@ -1 +1 @@
Subproject commit f29dcbe187b517d01964b1074eb7ff0b90849eed
Subproject commit 32a683071758ee59d47e2c5cb29c87882993facd