aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2019-07-03 12:39:38 +0100
committerGitHub <noreply@github.com>2019-07-03 12:39:38 +0100
commit8f2813279c5888e655ee6f50f198cf8cb11b0b50 (patch)
tree0aac9464d3ea87d37cd0689903ba08094b7c0984 /common
parentff958830d1097b9bfa3c3b34094e671741ef563d (diff)
parente27dc41a7646fd3377d2400059c916fbcc35119c (diff)
downloadnextpnr-8f2813279c5888e655ee6f50f198cf8cb11b0b50.tar.gz
nextpnr-8f2813279c5888e655ee6f50f198cf8cb11b0b50.tar.bz2
nextpnr-8f2813279c5888e655ee6f50f198cf8cb11b0b50.zip
Merge pull request #284 from YosysHQ/json_write
Initial support for writing to json files from nextpnr.
Diffstat (limited to 'common')
-rw-r--r--common/command.cc123
-rw-r--r--common/command.h10
-rw-r--r--common/nextpnr.cc115
-rw-r--r--common/nextpnr.h78
-rw-r--r--common/place_common.cc2
-rw-r--r--common/placer1.cc26
-rw-r--r--common/placer1.h6
-rw-r--r--common/placer_heap.cc13
-rw-r--r--common/placer_heap.h5
-rw-r--r--common/project.cc144
-rw-r--r--common/project.h45
-rw-r--r--common/pybindings.cc2
-rw-r--r--common/router1.cc10
-rw-r--r--common/router1.h5
-rw-r--r--common/settings.h74
-rw-r--r--common/timing.cc22
-rw-r--r--common/timing_opt.h6
17 files changed, 329 insertions, 357 deletions
diff --git a/common/command.cc b/common/command.cc
index 3eafdb17..8acbafd2 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -27,6 +27,7 @@
#include "pybindings.h"
#endif
+#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/program_options.hpp>
@@ -35,6 +36,7 @@
#include "command.h"
#include "design_utils.h"
#include "jsonparse.h"
+#include "jsonwrite.h"
#include "log.h"
#include "timing.h"
#include "util.h"
@@ -120,6 +122,7 @@ po::options_description CommandHandler::getGeneralOptions()
#endif
general.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
+ general.add_options()("write", po::value<std::string>(), "JSON design file to write");
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");
@@ -135,6 +138,9 @@ po::options_description CommandHandler::getGeneralOptions()
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()("no-route", "process design without routing");
+ general.add_options()("no-place", "process design without placement");
+ general.add_options()("no-pack", "process design without packing");
general.add_options()("ignore-loops", "ignore combinational loops in timing analysis");
@@ -143,13 +149,14 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("freq", po::value<double>(), "set target frequency for design in MHz");
general.add_options()("timing-allow-fail", "allow timing to fail in design");
general.add_options()("no-tmdriv", "disable timing-driven placement");
- general.add_options()("save", po::value<std::string>(), "project file to write");
- general.add_options()("load", po::value<std::string>(), "project file to read");
return general;
}
void CommandHandler::setupContext(Context *ctx)
{
+ if (ctx->settings.find(ctx->id("seed")) != ctx->settings.end())
+ ctx->rngstate = ctx->setting<uint64_t>("seed");
+
if (vm.count("verbose")) {
ctx->verbose = true;
}
@@ -177,9 +184,9 @@ void CommandHandler::setupContext(Context *ctx)
}
if (vm.count("slack_redist_iter")) {
- ctx->slack_redist_iter = vm["slack_redist_iter"].as<int>();
+ ctx->settings[ctx->id("slack_redist_iter")] = vm["slack_redist_iter"].as<std::string>();
if (vm.count("freq") && vm["freq"].as<double>() == 0) {
- ctx->auto_freq = true;
+ ctx->settings[ctx->id("auto_freq")] = std::to_string(true);
#ifndef NO_GUI
if (!vm.count("gui"))
#endif
@@ -188,11 +195,11 @@ void CommandHandler::setupContext(Context *ctx)
}
if (vm.count("ignore-loops")) {
- settings->set("timing/ignoreLoops", true);
+ ctx->settings[ctx->id("timing/ignoreLoops")] = std::to_string(true);
}
if (vm.count("timing-allow-fail")) {
- settings->set("timing/allowFail", true);
+ ctx->settings[ctx->id("timing/allowFail")] = std::to_string(true);
}
if (vm.count("placer")) {
@@ -201,30 +208,43 @@ void CommandHandler::setupContext(Context *ctx)
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);
+ ctx->settings[ctx->id("placer")] = placer;
}
if (vm.count("cstrweight")) {
- settings->set("placer1/constraintWeight", vm["cstrweight"].as<float>());
+ ctx->settings[ctx->id("placer1/constraintWeight")] = std::to_string(vm["cstrweight"].as<float>());
}
if (vm.count("starttemp")) {
- settings->set("placer1/startTemp", vm["starttemp"].as<float>());
+ ctx->settings[ctx->id("placer1/startTemp")] = std::to_string(vm["starttemp"].as<float>());
}
if (vm.count("placer-budgets")) {
- settings->set("placer1/budgetBased", true);
+ ctx->settings[ctx->id("placer1/budgetBased")] = std::to_string(true);
}
if (vm.count("freq")) {
auto freq = vm["freq"].as<double>();
if (freq > 0)
- ctx->target_freq = freq * 1e6;
+ ctx->settings[ctx->id("target_freq")] = std::to_string(freq * 1e6);
}
- ctx->timing_driven = true;
if (vm.count("no-tmdriv"))
- ctx->timing_driven = false;
+ ctx->settings[ctx->id("timing_driven")] = std::to_string(false);
+
+ // Setting default values
+ if (ctx->settings.find(ctx->id("target_freq")) == ctx->settings.end())
+ ctx->settings[ctx->id("target_freq")] = std::to_string(12e6);
+ if (ctx->settings.find(ctx->id("timing_driven")) == ctx->settings.end())
+ ctx->settings[ctx->id("timing_driven")] = std::to_string(true);
+ if (ctx->settings.find(ctx->id("slack_redist_iter")) == ctx->settings.end())
+ ctx->settings[ctx->id("slack_redist_iter")] = "0";
+ if (ctx->settings.find(ctx->id("auto_freq")) == ctx->settings.end())
+ ctx->settings[ctx->id("auto_freq")] = std::to_string(false);
+ if (ctx->settings.find(ctx->id("placer")) == ctx->settings.end())
+ ctx->settings[ctx->id("placer")] = Arch::defaultPlacer;
+
+ ctx->settings[ctx->id("arch.name")] = std::string(ctx->archId().c_str(ctx));
+ ctx->settings[ctx->id("arch.type")] = std::string(ctx->archArgsToId(ctx->archArgs()).c_str(ctx));
+ ctx->settings[ctx->id("seed")] = std::to_string(ctx->rngstate);
}
int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
@@ -237,19 +257,17 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv, (vm.count("gui-no-aa") > 0));
- MainWindow w(std::move(ctx), chipArgs);
+ MainWindow w(std::move(ctx), this);
try {
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename);
- w.notifyChangeContext();
if (!parse_json_file(f, filename, w.getContext()))
log_error("Loading design failed.\n");
customAfterLoad(w.getContext());
- w.updateLoaded();
- } else if (vm.count("load")) {
- w.projectLoad(vm["load"].as<std::string>());
+ w.notifyChangeContext();
+ w.updateActions();
} else
w.notifyChangeContext();
} catch (log_execution_error_exception) {
@@ -280,31 +298,42 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
execute_python_file(filename.c_str());
} else
#endif
- if (vm.count("json") || vm.count("load")) {
- run_script_hook("pre-pack");
- if (!ctx->pack() && !ctx->force)
- log_error("Packing design failed.\n");
+ if (vm.count("json")) {
+ bool do_pack = vm.count("pack-only") != 0 || vm.count("no-pack") == 0;
+ bool do_place = vm.count("pack-only") == 0 && vm.count("no-place") == 0;
+ bool do_route = vm.count("pack-only") == 0 && vm.count("no-route") == 0;
+
+ if (do_pack) {
+ run_script_hook("pre-pack");
+ if (!ctx->pack() && !ctx->force)
+ log_error("Packing design failed.\n");
+ }
assign_budget(ctx.get());
ctx->check();
print_utilisation(ctx.get());
- run_script_hook("pre-place");
- if (!vm.count("pack-only")) {
+ if (do_place) {
+ run_script_hook("pre-place");
if (!ctx->place() && !ctx->force)
log_error("Placing design failed.\n");
ctx->check();
- run_script_hook("pre-route");
+ }
+ if (do_route) {
+ run_script_hook("pre-route");
if (!ctx->route() && !ctx->force)
log_error("Routing design failed.\n");
+ run_script_hook("post-route");
}
- run_script_hook("post-route");
customBitstream(ctx.get());
}
- if (vm.count("save")) {
- project.save(ctx.get(), vm["save"].as<std::string>());
+ if (vm.count("write")) {
+ std::string filename = vm["write"].as<std::string>();
+ std::ofstream f(filename);
+ if (!write_json_file(f, filename, ctx.get()))
+ log_error("Saving design failed.\n");
}
#ifndef NO_PYTHON
@@ -341,13 +370,14 @@ int CommandHandler::exec()
if (executeBeforeContext())
return 0;
- std::unique_ptr<Context> ctx;
- if (vm.count("load") && vm.count("gui") == 0) {
- ctx = project.load(vm["load"].as<std::string>());
- } else {
- ctx = createContext();
+ std::unordered_map<std::string, Property> values;
+ if (vm.count("json")) {
+ std::string filename = vm["json"].as<std::string>();
+ std::ifstream f(filename);
+ if (!load_json_settings(f, filename, values))
+ log_error("Loading design failed.\n");
}
- settings = std::unique_ptr<Settings>(new Settings(ctx.get()));
+ std::unique_ptr<Context> ctx = createContext(values);
setupContext(ctx.get());
setupArchContext(ctx.get());
int rc = executeMain(std::move(ctx));
@@ -359,6 +389,27 @@ int CommandHandler::exec()
}
}
+std::unique_ptr<Context> CommandHandler::load_json(std::string filename)
+{
+ vm.clear();
+ std::unordered_map<std::string, Property> values;
+ {
+ std::ifstream f(filename);
+ if (!load_json_settings(f, filename, values))
+ log_error("Loading design failed.\n");
+ }
+ std::unique_ptr<Context> ctx = createContext(values);
+ setupContext(ctx.get());
+ setupArchContext(ctx.get());
+ {
+ std::ifstream f(filename);
+ if (!parse_json_file(f, filename, ctx.get()))
+ log_error("Loading design failed.\n");
+ }
+ customAfterLoad(ctx.get());
+ return ctx;
+}
+
void CommandHandler::run_script_hook(const std::string &name)
{
#ifndef NO_PYTHON
diff --git a/common/command.h b/common/command.h
index d0f1d328..e52b5a48 100644
--- a/common/command.h
+++ b/common/command.h
@@ -22,9 +22,9 @@
#define COMMAND_H
#include <boost/program_options.hpp>
+#include <fstream>
+#include "log.h"
#include "nextpnr.h"
-#include "project.h"
-#include "settings.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -37,10 +37,11 @@ class CommandHandler
virtual ~CommandHandler(){};
int exec();
+ std::unique_ptr<Context> load_json(std::string filename);
protected:
virtual void setupArchContext(Context *ctx) = 0;
- virtual std::unique_ptr<Context> createContext() = 0;
+ virtual std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) = 0;
virtual po::options_description getArchOptions() = 0;
virtual void validate(){};
virtual void customAfterLoad(Context *ctx){};
@@ -58,15 +59,12 @@ class CommandHandler
protected:
po::variables_map vm;
- ArchArgs chipArgs;
- std::unique_ptr<Settings> settings;
private:
po::options_description options;
po::positional_options_description pos;
int argc;
char **argv;
- ProjectHandler project;
std::ofstream logfile;
};
diff --git a/common/nextpnr.cc b/common/nextpnr.cc
index daaadf28..d4cc4917 100644
--- a/common/nextpnr.cc
+++ b/common/nextpnr.cc
@@ -18,6 +18,7 @@
*/
#include "nextpnr.h"
+#include <boost/algorithm/string.hpp>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -453,4 +454,118 @@ DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y)
return dxy;
}
+void BaseCtx::archInfoToAttributes()
+{
+ for (auto &cell : cells) {
+ auto ci = cell.second.get();
+ if (ci->bel != BelId()) {
+ if (ci->attrs.find(id("BEL")) != ci->attrs.end()) {
+ ci->attrs.erase(ci->attrs.find(id("BEL")));
+ }
+ ci->attrs[id("NEXTPNR_BEL")] = getCtx()->getBelName(ci->bel).c_str(this);
+ ci->attrs[id("BEL_STRENGTH")] = std::to_string((int)ci->belStrength);
+ }
+ if (ci->constr_x != ci->UNCONSTR)
+ ci->attrs[id("CONSTR_X")] = std::to_string(ci->constr_x);
+ if (ci->constr_y != ci->UNCONSTR)
+ ci->attrs[id("CONSTR_Y")] = std::to_string(ci->constr_y);
+ if (ci->constr_z != ci->UNCONSTR) {
+ ci->attrs[id("CONSTR_Z")] = std::to_string(ci->constr_z);
+ ci->attrs[id("CONSTR_ABS_Z")] = std::to_string(ci->constr_abs_z ? 1 : 0);
+ }
+ if (ci->constr_parent != nullptr)
+ ci->attrs[id("CONSTR_PARENT")] = ci->constr_parent->name.c_str(this);
+ if (!ci->constr_children.empty()) {
+ std::string constr = "";
+ for (auto &item : ci->constr_children) {
+ if (!constr.empty())
+ constr += std::string(";");
+ constr += item->name.c_str(this);
+ }
+ ci->attrs[id("CONSTR_CHILDREN")] = constr;
+ }
+ }
+ for (auto &net : getCtx()->nets) {
+ auto ni = net.second.get();
+ std::string routing;
+ bool first = true;
+ for (auto &item : ni->wires) {
+ if (!first)
+ routing += ";";
+ routing += getCtx()->getWireName(item.first).c_str(this);
+ routing += ";";
+ if (item.second.pip != PipId())
+ routing += getCtx()->getPipName(item.second.pip).c_str(this);
+ routing += ";" + std::to_string(item.second.strength);
+ first = false;
+ }
+ ni->attrs[id("ROUTING")] = routing;
+ }
+}
+
+void BaseCtx::attributesToArchInfo()
+{
+ for (auto &cell : cells) {
+ auto ci = cell.second.get();
+ auto val = ci->attrs.find(id("NEXTPNR_BEL"));
+ if (val != ci->attrs.end()) {
+ auto str = ci->attrs.find(id("BEL_STRENGTH"));
+ PlaceStrength strength = PlaceStrength::STRENGTH_USER;
+ if (str != ci->attrs.end())
+ strength = (PlaceStrength)std::stoi(str->second.str);
+
+ BelId b = getCtx()->getBelByName(id(val->second.str));
+ getCtx()->bindBel(b, ci, strength);
+ }
+ val = ci->attrs.find(id("CONSTR_X"));
+ if (val != ci->attrs.end())
+ ci->constr_x = std::stoi(val->second.str);
+
+ val = ci->attrs.find(id("CONSTR_Y"));
+ if (val != ci->attrs.end())
+ ci->constr_y = std::stoi(val->second.str);
+
+ val = ci->attrs.find(id("CONSTR_Z"));
+ if (val != ci->attrs.end())
+ ci->constr_z = std::stoi(val->second.str);
+
+ val = ci->attrs.find(id("CONSTR_ABS_Z"));
+ if (val != ci->attrs.end())
+ ci->constr_abs_z = std::stoi(val->second.str) == 1;
+
+ val = ci->attrs.find(id("CONSTR_PARENT"));
+ if (val != ci->attrs.end()) {
+ auto parent = cells.find(id(val->second.str));
+ if (parent != cells.end())
+ ci->constr_parent = parent->second.get();
+ }
+ val = ci->attrs.find(id("CONSTR_CHILDREN"));
+ if (val != ci->attrs.end()) {
+ std::vector<std::string> strs;
+ boost::split(strs, val->second.str, boost::is_any_of(";"));
+ for (auto val : strs) {
+ ci->constr_children.push_back(cells.find(id(val.c_str()))->second.get());
+ }
+ }
+ }
+ for (auto &net : getCtx()->nets) {
+ auto ni = net.second.get();
+ auto val = ni->attrs.find(id("ROUTING"));
+ if (val != ni->attrs.end()) {
+ std::vector<std::string> strs;
+ boost::split(strs, val->second.str, boost::is_any_of(";"));
+ for (size_t i = 0; i < strs.size() / 3; i++) {
+ std::string wire = strs[i * 3];
+ std::string pip = strs[i * 3 + 1];
+ PlaceStrength strength = (PlaceStrength)std::stoi(strs[i * 3 + 2]);
+ if (pip.empty())
+ getCtx()->bindWire(getCtx()->getWireByName(id(wire)), ni, strength);
+ else
+ getCtx()->bindPip(getCtx()->getPipByName(id(pip)), ni, strength);
+ }
+ }
+ }
+ getCtx()->assignArchInfo();
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/common/nextpnr.h b/common/nextpnr.h
index fc49300e..3f434580 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -32,6 +32,7 @@
#include <vector>
#include <boost/functional/hash.hpp>
+#include <boost/lexical_cast.hpp>
#ifndef NEXTPNR_H
#define NEXTPNR_H
@@ -286,6 +287,44 @@ struct PipMap
PlaceStrength strength = STRENGTH_NONE;
};
+struct Property
+{
+ bool is_string;
+
+ std::string str;
+ int num;
+
+ std::string::iterator begin() { return str.begin(); }
+ std::string::iterator end() { return str.end(); }
+
+ bool isString() const { return is_string; }
+
+ void setNumber(int val)
+ {
+ is_string = false;
+ num = val;
+ str = std::to_string(val);
+ }
+ void setString(std::string val)
+ {
+ is_string = true;
+ str = val;
+ }
+
+ const char *c_str() const { return str.c_str(); }
+ operator std::string() const { return str; }
+
+ bool operator==(const std::string other) const { return str == other; }
+ bool operator!=(const std::string other) const { return str != other; }
+
+ Property &operator=(std::string other)
+ {
+ is_string = true;
+ str = other;
+ return *this;
+ }
+};
+
struct ClockConstraint;
struct NetInfo : ArchNetInfo
@@ -295,7 +334,7 @@ struct NetInfo : ArchNetInfo
PortRef driver;
std::vector<PortRef> users;
- std::unordered_map<IdString, std::string> attrs;
+ std::unordered_map<IdString, Property> attrs;
// wire -> uphill_pip
std::unordered_map<WireId, PipMap> wires;
@@ -328,7 +367,7 @@ struct CellInfo : ArchCellInfo
int32_t udata;
std::unordered_map<IdString, PortInfo> ports;
- std::unordered_map<IdString, std::string> attrs, params;
+ std::unordered_map<IdString, Property> attrs, params;
BelId bel;
PlaceStrength belStrength = STRENGTH_NONE;
@@ -506,15 +545,21 @@ struct BaseCtx
mutable std::vector<const std::string *> *idstring_idx_to_str;
// Project settings and config switches
- std::unordered_map<IdString, std::string> settings;
+ std::unordered_map<IdString, Property> settings;
// Placed nets and cells.
std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
+ // Top-level ports
+ std::unordered_map<IdString, PortInfo> ports;
+
// Floorplanning regions
std::unordered_map<IdString, std::unique_ptr<Region>> region;
+ // Context meta data
+ std::unordered_map<IdString, Property> attrs;
+
BaseCtx()
{
idstring_str_to_idx = new std::unordered_map<std::string, int>;
@@ -646,6 +691,9 @@ struct BaseCtx
// Workaround for lack of wrappable constructors
DecalXY constructDecalXY(DecalId decal, float x, float y);
+
+ void archInfoToAttributes();
+ void attributesToArchInfo();
};
NEXTPNR_NAMESPACE_END
@@ -659,10 +707,6 @@ struct Context : Arch, DeterministicRNG
bool verbose = false;
bool debug = false;
bool force = false;
- bool timing_driven = true;
- float target_freq = 12e6;
- bool auto_freq = false;
- int slack_redist_iter = 0;
Context(ArchArgs args) : Arch(args) {}
@@ -683,6 +727,26 @@ struct Context : Arch, DeterministicRNG
void check() const;
void archcheck() const;
+
+ template <typename T> T setting(const char *name, T defaultValue)
+ {
+ IdString new_id = id(name);
+ if (settings.find(new_id) != settings.end())
+ return boost::lexical_cast<T>(settings.find(new_id)->second.str);
+ else
+ settings[id(name)] = std::to_string(defaultValue);
+
+ return defaultValue;
+ }
+
+ template <typename T> T setting(const char *name) const
+ {
+ IdString new_id = id(name);
+ if (settings.find(new_id) != settings.end())
+ return boost::lexical_cast<T>(settings.find(new_id)->second.str);
+ else
+ throw std::runtime_error("settings does not exists");
+ }
};
NEXTPNR_NAMESPACE_END
diff --git a/common/place_common.cc b/common/place_common.cc
index 73a320d0..cb9799b5 100644
--- a/common/place_common.cc
+++ b/common/place_common.cc
@@ -37,7 +37,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
if (driver_gb)
return 0;
int clock_count;
- bool timing_driven = ctx->timing_driven && type == MetricType::COST &&
+ bool timing_driven = ctx->setting<bool>("timing_driven") && type == MetricType::COST &&
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
delay_t negative_slack = 0;
delay_t worst_slack = std::numeric_limits<delay_t>::max();
diff --git a/common/placer1.cc b/common/placer1.cc
index a8ddd8a6..cb7ae847 100644
--- a/common/placer1.cc
+++ b/common/placer1.cc
@@ -219,7 +219,7 @@ class SAPlacer
if ((placed_cells - constr_placed_cells) % 500 != 0)
log_info(" initial placement placed %d/%d cells\n", int(placed_cells - constr_placed_cells),
int(autoplaced.size()));
- if (cfg.budgetBased && ctx->slack_redist_iter > 0)
+ if (cfg.budgetBased && cfg.slack_redist_iter > 0)
assign_budget(ctx);
ctx->yield();
auto iplace_end = std::chrono::high_resolution_clock::now();
@@ -370,16 +370,16 @@ class SAPlacer
ctx->shuffle(autoplaced);
// Legalisation is a big change so force a slack redistribution here
- if (ctx->slack_redist_iter > 0 && cfg.budgetBased)
+ if (cfg.slack_redist_iter > 0 && cfg.budgetBased)
assign_budget(ctx, true /* quiet */);
}
require_legal = false;
- } else if (cfg.budgetBased && ctx->slack_redist_iter > 0 && iter % ctx->slack_redist_iter == 0) {
+ } else if (cfg.budgetBased && cfg.slack_redist_iter > 0 && iter % cfg.slack_redist_iter == 0) {
assign_budget(ctx, true /* quiet */);
}
// Invoke timing analysis to obtain criticalities
- if (!cfg.budgetBased && ctx->timing_driven)
+ if (!cfg.budgetBased && cfg.timing_driven)
get_criticalities(ctx, &net_crit);
// Need to rebuild costs after criticalities change
setup_costs();
@@ -804,7 +804,7 @@ class SAPlacer
if (ignore_net(ni))
continue;
net_bounds[ni->udata] = get_net_bounds(ni);
- if (ctx->timing_driven && int(ni->users.size()) < cfg.timingFanoutThresh)
+ if (cfg.timing_driven && int(ni->users.size()) < cfg.timingFanoutThresh)
for (size_t i = 0; i < ni->users.size(); i++)
net_arc_tcost[ni->udata][i] = get_timing_cost(ni, i);
}
@@ -1021,7 +1021,7 @@ class SAPlacer
}
}
- if (ctx->timing_driven && int(pn->users.size()) < cfg.timingFanoutThresh) {
+ if (cfg.timing_driven && int(pn->users.size()) < cfg.timingFanoutThresh) {
// Output ports - all arcs change timing
if (port.second.type == PORT_OUT) {
int cc;
@@ -1061,7 +1061,7 @@ class SAPlacer
if (md.already_bounds_changed_x[bc] == MoveChangeData::NO_CHANGE)
md.wirelen_delta += md.new_net_bounds[bc].hpwl() - net_bounds[bc].hpwl();
- if (ctx->timing_driven) {
+ if (cfg.timing_driven) {
for (const auto &tc : md.changed_arcs) {
double old_cost = net_arc_tcost.at(tc.first).at(tc.second);
double new_cost = get_timing_cost(net_by_udata.at(tc.first), tc.second);
@@ -1131,13 +1131,15 @@ class SAPlacer
Placer1Cfg cfg;
};
-Placer1Cfg::Placer1Cfg(Context *ctx) : Settings(ctx)
+Placer1Cfg::Placer1Cfg(Context *ctx)
{
- constraintWeight = get<float>("placer1/constraintWeight", 10);
- minBelsForGridPick = get<int>("placer1/minBelsForGridPick", 64);
- budgetBased = get<bool>("placer1/budgetBased", false);
- startTemp = get<float>("placer1/startTemp", 1);
+ constraintWeight = ctx->setting<float>("placer1/constraintWeight", 10);
+ minBelsForGridPick = ctx->setting<int>("placer1/minBelsForGridPick", 64);
+ budgetBased = ctx->setting<bool>("placer1/budgetBased", false);
+ startTemp = ctx->setting<float>("placer1/startTemp", 1);
timingFanoutThresh = std::numeric_limits<int>::max();
+ timing_driven = ctx->setting<bool>("timing_driven");
+ slack_redist_iter = ctx->setting<int>("slack_redist_iter");
}
bool placer1(Context *ctx, Placer1Cfg cfg)
diff --git a/common/placer1.h b/common/placer1.h
index 4c7c7339..1356db3e 100644
--- a/common/placer1.h
+++ b/common/placer1.h
@@ -19,12 +19,12 @@
#ifndef PLACE_H
#define PLACE_H
+#include "log.h"
#include "nextpnr.h"
-#include "settings.h"
NEXTPNR_NAMESPACE_BEGIN
-struct Placer1Cfg : public Settings
+struct Placer1Cfg
{
Placer1Cfg(Context *ctx);
float constraintWeight;
@@ -32,6 +32,8 @@ struct Placer1Cfg : public Settings
bool budgetBased;
float startTemp;
int timingFanoutThresh;
+ bool timing_driven;
+ int slack_redist_iter;
};
extern bool placer1(Context *ctx, Placer1Cfg cfg);
diff --git a/common/placer_heap.cc b/common/placer_heap.cc
index f9b639f8..cd800085 100644
--- a/common/placer_heap.cc
+++ b/common/placer_heap.cc
@@ -234,7 +234,7 @@ class HeAPPlacer
std::chrono::duration<double>(run_stopt - run_startt).count());
}
- if (ctx->timing_driven)
+ if (cfg.timing_driven)
get_criticalities(ctx, &net_crit);
if (legal_hpwl < best_hpwl) {
@@ -1516,11 +1516,12 @@ int HeAPPlacer::CutSpreader::seq = 0;
bool placer_heap(Context *ctx, PlacerHeapCfg cfg) { return HeAPPlacer(ctx, cfg).place(); }
-PlacerHeapCfg::PlacerHeapCfg(Context *ctx) : Settings(ctx)
+PlacerHeapCfg::PlacerHeapCfg(Context *ctx)
{
- alpha = get<float>("placerHeap/alpha", 0.1);
- criticalityExponent = get<int>("placerHeap/criticalityExponent", 2);
- timingWeight = get<int>("placerHeap/timingWeight", 10);
+ alpha = ctx->setting<float>("placerHeap/alpha", 0.1);
+ criticalityExponent = ctx->setting<int>("placerHeap/criticalityExponent", 2);
+ timingWeight = ctx->setting<int>("placerHeap/timingWeight", 10);
+ timing_driven = ctx->setting<bool>("timing_driven");
}
NEXTPNR_NAMESPACE_END
@@ -1538,7 +1539,7 @@ bool placer_heap(Context *ctx, PlacerHeapCfg cfg)
return false;
}
-PlacerHeapCfg::PlacerHeapCfg(Context *ctx) : Settings(ctx) {}
+PlacerHeapCfg::PlacerHeapCfg(Context *ctx) {}
NEXTPNR_NAMESPACE_END
diff --git a/common/placer_heap.h b/common/placer_heap.h
index 841aa0d9..a018aef8 100644
--- a/common/placer_heap.h
+++ b/common/placer_heap.h
@@ -26,18 +26,19 @@
#ifndef PLACER_HEAP_H
#define PLACER_HEAP_H
+#include "log.h"
#include "nextpnr.h"
-#include "settings.h"
NEXTPNR_NAMESPACE_BEGIN
-struct PlacerHeapCfg : public Settings
+struct PlacerHeapCfg
{
PlacerHeapCfg(Context *ctx);
float alpha;
float criticalityExponent;
float timingWeight;
+ bool timing_driven;
std::unordered_set<IdString> ioBufTypes;
};
diff --git a/common/project.cc b/common/project.cc
deleted file mode 100644
index b0ebe961..00000000
--- a/common/project.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@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.
- *
- */
-
-#include "project.h"
-#include <algorithm>
-#include <boost/filesystem/convenience.hpp>
-#include <boost/property_tree/json_parser.hpp>
-#include <fstream>
-#include "jsonparse.h"
-#include "log.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-boost::filesystem::path make_relative(boost::filesystem::path child, boost::filesystem::path parent)
-{
- boost::filesystem::path::const_iterator parentIter = parent.begin();
- boost::filesystem::path::const_iterator childIter = child.begin();
-
- while (parentIter != parent.end() && childIter != child.end() && (*childIter) == (*parentIter)) {
- ++childIter;
- ++parentIter;
- }
-
- boost::filesystem::path finalPath;
- while (parentIter != parent.end()) {
- finalPath /= "..";
- ++parentIter;
- }
-
- while (childIter != child.end()) {
- finalPath /= *childIter;
- ++childIter;
- }
-
- return finalPath;
-}
-
-void ProjectHandler::save(Context *ctx, std::string filename)
-{
- try {
- boost::filesystem::path proj(filename);
- std::ofstream f(filename);
- pt::ptree root;
-
- log_info("Saving project %s...\n", filename.c_str());
- log_break();
-
- root.put("project.version", 1);
- root.put("project.name", boost::filesystem::basename(filename));
- root.put("project.arch.name", ctx->archId().c_str(ctx));
- root.put("project.arch.type", ctx->archArgsToId(ctx->archArgs()).c_str(ctx));
- std::string fn = ctx->settings[ctx->id("input/json")];
- root.put("project.input.json", make_relative(fn, proj.parent_path()).string());
- root.put("project.params.freq", int(ctx->target_freq / 1e6));
- root.put("project.params.seed", ctx->rngstate);
- saveArch(ctx, root, proj.parent_path().string());
- for (auto const &item : ctx->settings) {
- std::string path = "project.settings.";
- path += item.first.c_str(ctx);
- std::replace(path.begin(), path.end(), '/', '.');
- root.put(path, item.second);
- }
- pt::write_json(f, root);
- } catch (...) {
- log_error("Error saving project file.\n");
- }
-}
-
-void addSettings(Context *ctx, std::string path, pt::ptree sub)
-{
- for (pt::ptree::value_type &v : sub) {
- const std::string &key = v.first;
- const boost::property_tree::ptree &subtree = v.second;
- if (subtree.empty()) {
- ctx->settings.emplace(ctx->id(path + key), subtree.get_value<std::string>().c_str());
- } else {
- addSettings(ctx, path + key + "/", subtree);
- }
- }
-}
-
-std::unique_ptr<Context> ProjectHandler::load(std::string filename)
-{
- std::unique_ptr<Context> ctx;
- try {
- pt::ptree root;
- boost::filesystem::path proj(filename);
- pt::read_json(filename, root);
- log_info("Loading project %s...\n", filename.c_str());
- log_break();
-
- int version = root.get<int>("project.version");
- if (version != 1)
- log_error("Wrong project format version.\n");
-
- ctx = createContext(root);
-
- std::string arch_name = root.get<std::string>("project.arch.name");
- if (arch_name != ctx->archId().c_str(ctx.get()))
- log_error("Unsuported project architecture.\n");
-
- auto project = root.get_child("project");
- auto input = project.get_child("input");
- std::string fn = input.get<std::string>("json");
- boost::filesystem::path json = proj.parent_path() / fn;
- std::ifstream f(json.string());
- if (!parse_json_file(f, fn, ctx.get()))
- log_error("Loading design failed.\n");
-
- if (project.count("params")) {
- auto params = project.get_child("params");
- if (params.count("freq"))
- ctx->target_freq = params.get<double>("freq") * 1e6;
- if (params.count("seed"))
- ctx->rngseed(params.get<uint64_t>("seed"));
- }
- if (project.count("settings")) {
- addSettings(ctx.get(), "", project.get_child("settings"));
- }
-
- loadArch(ctx.get(), root, proj.parent_path().string());
- } catch (...) {
- log_error("Error loading project file.\n");
- }
- return ctx;
-}
-
-NEXTPNR_NAMESPACE_END
diff --git a/common/project.h b/common/project.h
deleted file mode 100644
index 03b4b7b5..00000000
--- a/common/project.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@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.
- *
- */
-
-#ifndef PROJECT_H
-#define PROJECT_H
-
-#include <boost/filesystem/convenience.hpp>
-#include <boost/property_tree/ptree.hpp>
-#include "nextpnr.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-namespace pt = boost::property_tree;
-
-struct ProjectHandler
-{
- void save(Context *ctx, std::string filename);
- std::unique_ptr<Context> load(std::string filename);
- // implemented per arch
- void saveArch(Context *ctx, pt::ptree &root, std::string path);
- std::unique_ptr<Context> createContext(pt::ptree &root);
- void loadArch(Context *ctx, pt::ptree &root, std::string path);
-};
-
-boost::filesystem::path make_relative(boost::filesystem::path child, boost::filesystem::path parent);
-
-NEXTPNR_NAMESPACE_END
-
-#endif // PROJECT_H
diff --git a/common/pybindings.cc b/common/pybindings.cc
index 60f87e27..52dd9717 100644
--- a/common/pybindings.cc
+++ b/common/pybindings.cc
@@ -122,7 +122,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
.value("PORT_INOUT", PORT_INOUT)
.export_values();
- typedef std::unordered_map<IdString, std::string> AttrMap;
+ typedef std::unordered_map<IdString, Property> 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;
diff --git a/common/router1.cc b/common/router1.cc
index 28a422c8..02c817c7 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -733,12 +733,12 @@ struct Router1
NEXTPNR_NAMESPACE_BEGIN
-Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx)
+Router1Cfg::Router1Cfg(Context *ctx)
{
- maxIterCnt = get<int>("router1/maxIterCnt", 200);
- cleanupReroute = get<bool>("router1/cleanupReroute", true);
- fullCleanupReroute = get<bool>("router1/fullCleanupReroute", true);
- useEstimate = get<bool>("router1/useEstimate", true);
+ maxIterCnt = ctx->setting<int>("router1/maxIterCnt", 200);
+ cleanupReroute = ctx->setting<bool>("router1/cleanupReroute", true);
+ fullCleanupReroute = ctx->setting<bool>("router1/fullCleanupReroute", true);
+ useEstimate = ctx->setting<bool>("router1/useEstimate", true);
wireRipupPenalty = ctx->getRipupDelayPenalty();
netRipupPenalty = 10 * ctx->getRipupDelayPenalty();
diff --git a/common/router1.h b/common/router1.h
index 80d7aa96..a37784f4 100644
--- a/common/router1.h
+++ b/common/router1.h
@@ -20,12 +20,11 @@
#ifndef ROUTER1_H
#define ROUTER1_H
+#include "log.h"
#include "nextpnr.h"
-#include "settings.h"
-
NEXTPNR_NAMESPACE_BEGIN
-struct Router1Cfg : Settings
+struct Router1Cfg
{
Router1Cfg(Context *ctx);
diff --git a/common/settings.h b/common/settings.h
deleted file mode 100644
index b57947c9..00000000
--- a/common/settings.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@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.
- *
- */
-#ifndef SETTINGS_H
-#define SETTINGS_H
-
-#include <boost/lexical_cast.hpp>
-#include "log.h"
-#include "nextpnr.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-class Settings
-{
- public:
- explicit Settings(Context *ctx) : ctx(ctx) {}
-
- template <typename T> T get(const char *name, T defaultValue)
- {
- try {
- IdString id = ctx->id(name);
- auto pair = ctx->settings.emplace(id, std::to_string(defaultValue));
- if (!pair.second) {
- return boost::lexical_cast<T>(pair.first->second);
- }
-
- } catch (boost::bad_lexical_cast &) {
- log_error("Problem reading setting %s, using default value\n", name);
- }
- return defaultValue;
- }
-
- 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
diff --git a/common/timing.cc b/common/timing.cc
index e67ac231..599d6dbd 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -113,7 +113,7 @@ struct Timing
Timing(Context *ctx, bool net_delays, bool update, CriticalPathMap *crit_path = nullptr,
DelayFrequency *slack_histogram = nullptr, NetCriticalityMap *net_crit = nullptr)
- : ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->target_freq),
+ : ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->setting<float>("target_freq")),
crit_path(crit_path), slack_histogram(slack_histogram), net_crit(net_crit),
async_clock(ctx->id("$async$"))
{
@@ -121,7 +121,7 @@ struct Timing
delay_t walk_paths()
{
- const auto clk_period = ctx->getDelayFromNS(1.0e9 / ctx->target_freq).maxDelay();
+ const auto clk_period = ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq")).maxDelay();
// First, compute the topographical order of nets to walk through the circuit, assuming it is a _acyclic_ graph
// TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops
@@ -658,17 +658,18 @@ void assign_budget(Context *ctx, bool quiet)
{
if (!quiet) {
log_break();
- log_info("Annotating ports with timing budgets for target frequency %.2f MHz\n", ctx->target_freq / 1e6);
+ log_info("Annotating ports with timing budgets for target frequency %.2f MHz\n",
+ ctx->setting<float>("target_freq") / 1e6);
}
- Timing timing(ctx, ctx->slack_redist_iter > 0 /* net_delays */, true /* update */);
+ Timing timing(ctx, ctx->setting<int>("slack_redist_iter") > 0 /* net_delays */, true /* update */);
timing.assign_budget();
if (!quiet || ctx->verbose) {
for (auto &net : ctx->nets) {
for (auto &user : net.second->users) {
// Post-update check
- if (!ctx->auto_freq && user.budget < 0)
+ if (!ctx->setting<bool>("auto_freq") && user.budget < 0)
log_info("port %s.%s, connected to net '%s', has negative "
"timing budget of %fns\n",
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
@@ -684,13 +685,14 @@ void assign_budget(Context *ctx, bool quiet)
// For slack redistribution, if user has not specified a frequency dynamically adjust the target frequency to be the
// currently achieved maximum
- if (ctx->auto_freq && ctx->slack_redist_iter > 0) {
- delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->target_freq);
- ctx->target_freq = 1.0e9 / ctx->getDelayNS(default_slack - timing.min_slack);
+ if (ctx->setting<bool>("auto_freq") && ctx->setting<int>("slack_redist_iter") > 0) {
+ delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->setting<float>("target_freq"));
+ ctx->settings[ctx->id("target_freq")] =
+ std::to_string(1.0e9 / ctx->getDelayNS(default_slack - timing.min_slack));
if (ctx->verbose)
log_info("minimum slack for this assign = %.2f ns, target Fmax for next "
"update = %.2f MHz\n",
- ctx->getDelayNS(timing.min_slack), ctx->target_freq / 1e6);
+ ctx->getDelayNS(timing.min_slack), ctx->setting<float>("target_freq") / 1e6);
}
if (!quiet)
@@ -897,7 +899,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
for (auto &clock : clock_reports) {
const auto &clock_name = clock.first.str(ctx);
const int width = max_width - clock_name.size();
- float target = ctx->target_freq / 1e6;
+ float target = ctx->setting<float>("target_freq") / 1e6;
if (ctx->nets.at(clock.first)->clkconstr)
target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
diff --git a/common/timing_opt.h b/common/timing_opt.h
index ceb35c71..775d9596 100644
--- a/common/timing_opt.h
+++ b/common/timing_opt.h
@@ -17,14 +17,14 @@
*
*/
+#include "log.h"
#include "nextpnr.h"
-#include "settings.h"
NEXTPNR_NAMESPACE_BEGIN
-struct TimingOptCfg : public Settings
+struct TimingOptCfg
{
- TimingOptCfg(Context *ctx) : Settings(ctx) {}
+ TimingOptCfg(Context *ctx) {}
// The timing optimiser will *only* optimise cells of these types
// Normally these would only be logic cells (or tiles if applicable), the algorithm makes little sense