aboutsummaryrefslogtreecommitdiffstats
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
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.
-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
-rw-r--r--ecp5/arch.cc5
-rw-r--r--ecp5/globals.cc2
-rw-r--r--ecp5/lpf.cc2
-rw-r--r--ecp5/main.cc128
-rw-r--r--ecp5/pack.cc12
-rw-r--r--ecp5/project.cc55
-rw-r--r--generic/arch.cc13
-rw-r--r--generic/main.cc10
-rw-r--r--generic/pack.cc1
-rw-r--r--gui/base.qrc1
-rw-r--r--gui/basewindow.cc156
-rw-r--r--gui/basewindow.h26
-rwxr-xr-xgui/create_img.sh5
-rw-r--r--gui/ecp5/mainwindow.cc46
-rw-r--r--gui/ecp5/mainwindow.h13
-rw-r--r--gui/generic/mainwindow.cc5
-rw-r--r--gui/generic/mainwindow.h2
-rw-r--r--gui/ice40/mainwindow.cc19
-rw-r--r--gui/ice40/mainwindow.h6
-rw-r--r--gui/resources/save_json.pngbin0 -> 1546 bytes
-rw-r--r--gui/worker.cc4
-rw-r--r--ice40/arch.cc16
-rw-r--r--ice40/main.cc55
-rw-r--r--ice40/pack.cc39
-rw-r--r--ice40/pcf.cc2
-rw-r--r--ice40/project.cc76
-rw-r--r--json/jsonparse.cc256
-rw-r--r--json/jsonparse.h3
-rw-r--r--json/jsonwrite.cc180
-rw-r--r--json/jsonwrite.h (renamed from generic/project.cc)22
47 files changed, 1042 insertions, 804 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
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 91db8d81..5b3cc660 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -526,12 +526,15 @@ bool Arch::place()
}
permute_luts();
+ getCtx()->settings[getCtx()->id("place")] = "1";
+ archInfoToAttributes();
return true;
}
bool Arch::route()
{
route_ecp5_globals(getCtx());
+ assignArchInfo();
assign_budget(getCtx(), true);
bool result = router1(getCtx(), Router1Cfg(getCtx()));
@@ -560,6 +563,8 @@ bool Arch::route()
log_info(" base %d adder %d\n", speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_base_delay,
speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_fanout_adder);
#endif
+ getCtx()->settings[getCtx()->id("route")] = "1";
+ archInfoToAttributes();
return result;
}
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index fae2c683..026f3a85 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -382,7 +382,7 @@ class Ecp5GlobalRouter
glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx));
glbnet->driver.cell = dcc.get();
glbnet->driver.port = id_CLKO;
- glbnet->is_global = true;
+ glbnet->attrs[ctx->id("ECP5_IS_GLOBAL")] = "1";
dcc->ports[id_CLKO].net = glbnet.get();
std::vector<PortRef> keep_users;
diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc
index 4ac70fc9..ceb1d7ae 100644
--- a/ecp5/lpf.cc
+++ b/ecp5/lpf.cc
@@ -133,7 +133,7 @@ bool Arch::applyLPF(std::string filename, std::istream &in)
}
if (!isempty(linebuf))
log_error("unexpected end of LPF file\n");
- settings.emplace(id("input/lpf"), filename);
+ settings[id("input/lpf")] = filename;
return true;
} catch (log_execution_error_exception) {
return false;
diff --git a/ecp5/main.cc b/ecp5/main.cc
index bb18aa58..75126cea 100644
--- a/ecp5/main.cc
+++ b/ecp5/main.cc
@@ -34,7 +34,7 @@ class ECP5CommandHandler : public CommandHandler
public:
ECP5CommandHandler(int argc, char **argv);
virtual ~ECP5CommandHandler(){};
- std::unique_ptr<Context> createContext() override;
+ std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override;
void setupArchContext(Context *ctx) override{};
void customAfterLoad(Context *ctx) override;
void validate() override;
@@ -98,9 +98,25 @@ void ECP5CommandHandler::customBitstream(Context *ctx)
write_bitstream(ctx, basecfg, textcfg);
}
-std::unique_ptr<Context> ECP5CommandHandler::createContext()
+static std::string speedString(ArchArgs::SpeedGrade speed)
{
- chipArgs.type = ArchArgs::LFE5U_45F;
+ switch (speed) {
+ case ArchArgs::SPEED_6:
+ return "6";
+ case ArchArgs::SPEED_7:
+ return "7";
+ case ArchArgs::SPEED_8:
+ return "8";
+ case ArchArgs::SPEED_8_5G:
+ return "8";
+ }
+ return "";
+}
+
+std::unique_ptr<Context> ECP5CommandHandler::createContext(std::unordered_map<std::string, Property> &values)
+{
+ ArchArgs chipArgs;
+ chipArgs.type = ArchArgs::NONE;
if (vm.count("25k"))
chipArgs.type = ArchArgs::LFE5U_25F;
@@ -122,34 +138,96 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
chipArgs.type = ArchArgs::LFE5UM5G_85F;
if (vm.count("package"))
chipArgs.package = vm["package"].as<std::string>();
- else
+
+ if (vm.count("speed")) {
+ int speed = vm["speed"].as<int>();
+ switch (speed) {
+ case 6:
+ chipArgs.speed = ArchArgs::SPEED_6;
+ break;
+ case 7:
+ chipArgs.speed = ArchArgs::SPEED_7;
+ break;
+ case 8:
+ chipArgs.speed = ArchArgs::SPEED_8;
+ break;
+ default:
+ log_error("Unsupported speed grade '%d'\n", speed);
+ }
+ } else {
+ if (chipArgs.type == ArchArgs::LFE5UM5G_25F || chipArgs.type == ArchArgs::LFE5UM5G_45F ||
+ chipArgs.type == ArchArgs::LFE5UM5G_85F) {
+ chipArgs.speed = ArchArgs::SPEED_8;
+ } else
+ chipArgs.speed = ArchArgs::SPEED_6;
+ }
+ if (values.find("arch.name") != values.end()) {
+ std::string arch_name = values["arch.name"].str;
+ if (arch_name != "ecp5")
+ log_error("Unsuported architecture '%s'.\n", arch_name.c_str());
+ }
+ if (values.find("arch.type") != values.end()) {
+ std::string arch_type = values["arch.type"].str;
+ if (chipArgs.type != ArchArgs::NONE)
+ log_error("Overriding architecture is unsuported.\n");
+
+ if (arch_type == "lfe5u_25f")
+ chipArgs.type = ArchArgs::LFE5U_25F;
+ if (arch_type == "lfe5u_45f")
+ chipArgs.type = ArchArgs::LFE5U_45F;
+ if (arch_type == "lfe5u_85f")
+ chipArgs.type = ArchArgs::LFE5U_85F;
+ if (arch_type == "lfe5um_25f")
+ chipArgs.type = ArchArgs::LFE5UM_25F;
+ if (arch_type == "lfe5um_45f")
+ chipArgs.type = ArchArgs::LFE5UM_45F;
+ if (arch_type == "lfe5um_85f")
+ chipArgs.type = ArchArgs::LFE5UM_85F;
+ if (arch_type == "lfe5um5g_25f")
+ chipArgs.type = ArchArgs::LFE5UM5G_25F;
+ if (arch_type == "lfe5um5g_45f")
+ chipArgs.type = ArchArgs::LFE5UM5G_45F;
+ if (arch_type == "lfe5um5g_85f")
+ chipArgs.type = ArchArgs::LFE5UM5G_85F;
+
+ if (chipArgs.type == ArchArgs::NONE)
+ log_error("Unsuported FPGA type '%s'.\n", arch_type.c_str());
+ }
+ if (values.find("arch.package") != values.end()) {
+ if (vm.count("package"))
+ log_error("Overriding architecture is unsuported.\n");
+ chipArgs.package = values["arch.package"].str;
+ }
+ if (values.find("arch.speed") != values.end()) {
+ std::string arch_speed = values["arch.speed"].str;
+ if (arch_speed == "6")
+ chipArgs.speed = ArchArgs::SPEED_6;
+ else if (arch_speed == "7")
+ chipArgs.speed = ArchArgs::SPEED_7;
+ else if (arch_speed == "8")
+ chipArgs.speed = ArchArgs::SPEED_8;
+ else
+ log_error("Unsuported speed '%s'.\n", arch_speed.c_str());
+ }
+ if (chipArgs.type == ArchArgs::NONE)
+ chipArgs.type = ArchArgs::LFE5U_45F;
+
+ if (chipArgs.package.empty())
chipArgs.package = "CABGA381";
+
if (chipArgs.type == ArchArgs::LFE5UM5G_25F || chipArgs.type == ArchArgs::LFE5UM5G_45F ||
chipArgs.type == ArchArgs::LFE5UM5G_85F) {
- if (vm.count("speed") && vm["speed"].as<int>() != 8)
+ if (chipArgs.speed != ArchArgs::SPEED_8)
log_error("Only speed grade 8 is available for 5G parts\n");
- chipArgs.speed = ArchArgs::SPEED_8_5G;
- } else {
- if (vm.count("speed")) {
- int speed = vm["speed"].as<int>();
- switch (speed) {
- case 6:
- chipArgs.speed = ArchArgs::SPEED_6;
- break;
- case 7:
- chipArgs.speed = ArchArgs::SPEED_7;
- break;
- case 8:
- chipArgs.speed = ArchArgs::SPEED_8;
- break;
- default:
- log_error("Unsupported speed grade '%d'\n", speed);
- }
- } else {
- chipArgs.speed = ArchArgs::SPEED_6;
- }
+ else
+ chipArgs.speed = ArchArgs::SPEED_8_5G;
}
+
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
+ for (auto &val : values)
+ ctx->settings[ctx->id(val.first)] = val.second;
+ ctx->settings[ctx->id("arch.package")] = ctx->archArgs().package;
+ ctx->settings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed);
return ctx;
}
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 7f00de1f..9e2bc326 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -1508,7 +1508,8 @@ class Ecp5Packer
std::unique_ptr<NetInfo> promoted_ecknet(new NetInfo);
promoted_ecknet->name = eckname;
- promoted_ecknet->is_global = true; // Prevents router etc touching this special net
+ promoted_ecknet->attrs[ctx->id("ECP5_IS_GLOBAL")] =
+ "1"; // Prevents router etc touching this special net
eclk.buf = promoted_ecknet.get();
NPNR_ASSERT(!ctx->nets.count(eckname));
ctx->nets[eckname] = std::move(promoted_ecknet);
@@ -1654,7 +1655,7 @@ class Ecp5Packer
port.c_str(ctx), ci->name.c_str(ctx), usr.port.c_str(ctx),
usr.cell->name.c_str(ctx));
}
- pn->is_global = true;
+ pn->attrs[ctx->id("ECP5_IS_GLOBAL")] = "1";
}
for (auto zport :
@@ -1896,7 +1897,7 @@ class Ecp5Packer
iol->params[ctx->id("DELAY.DEL_VALUE")] =
std::to_string(lookup_delay(str_or_default(ci->params, ctx->id("DEL_MODE"), "USER_DEFINED")));
if (ci->params.count(ctx->id("DEL_VALUE")) &&
- ci->params.at(ctx->id("DEL_VALUE")).substr(0, 5) != "DELAY")
+ std::string(ci->params.at(ctx->id("DEL_VALUE"))).substr(0, 5) != "DELAY")
iol->params[ctx->id("DELAY.DEL_VALUE")] = ci->params.at(ctx->id("DEL_VALUE"));
if (ci->ports.count(id_LOADN))
replace_port(ci, id_LOADN, iol, id_LOADN);
@@ -2428,6 +2429,8 @@ bool Arch::pack()
Ecp5Packer(ctx).pack();
log_info("Checksum: 0x%08x\n", ctx->checksum());
assignArchInfo();
+ ctx->settings[ctx->id("pack")] = "1";
+ archInfoToAttributes();
return true;
} catch (log_execution_error_exception) {
assignArchInfo();
@@ -2469,6 +2472,9 @@ void Arch::assignArchInfo()
ci->sliceInfo.has_l6mux = true;
}
}
+ for (auto net : sorted(nets)) {
+ net.second->is_global = bool_or_default(net.second->attrs, id("ECP5_IS_GLOBAL"));
+ }
}
NEXTPNR_NAMESPACE_END
diff --git a/ecp5/project.cc b/ecp5/project.cc
deleted file mode 100644
index 43318b1c..00000000
--- a/ecp5/project.cc
+++ /dev/null
@@ -1,55 +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 <boost/filesystem/convenience.hpp>
-#include <boost/property_tree/json_parser.hpp>
-#include <fstream>
-#include "log.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
-{
- root.put("project.arch.package", ctx->archArgs().package);
- root.put("project.arch.speed", ctx->archArgs().speed);
-}
-
-std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
-{
- ArchArgs chipArgs;
- std::string arch_type = root.get<std::string>("project.arch.type");
- if (arch_type == "25k") {
- chipArgs.type = ArchArgs::LFE5U_25F;
- }
- if (arch_type == "45k") {
- chipArgs.type = ArchArgs::LFE5U_45F;
- }
- if (arch_type == "85k") {
- chipArgs.type = ArchArgs::LFE5U_85F;
- }
- chipArgs.package = root.get<std::string>("project.arch.package");
- chipArgs.speed = ArchArgs::SpeedGrade(root.get<int>("project.arch.speed"));
-
- return std::unique_ptr<Context>(new Context(chipArgs));
-}
-
-void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {}
-
-NEXTPNR_NAMESPACE_END
diff --git a/generic/arch.cc b/generic/arch.cc
index 5617fa63..fa3c825d 100644
--- a/generic/arch.cc
+++ b/generic/arch.cc
@@ -496,13 +496,22 @@ 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()));
+ bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
+ getCtx()->settings[getCtx()->id("place")] = "1";
+ archInfoToAttributes();
+ return retVal;
} else {
log_error("Generic architecture does not support placer '%s'\n", placer.c_str());
}
}
-bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
+bool Arch::route()
+{
+ bool retVal = router1(getCtx(), Router1Cfg(getCtx()));
+ getCtx()->settings[getCtx()->id("route")] = "1";
+ archInfoToAttributes();
+ return retVal;
+}
// ---------------------------------------------------------------
diff --git a/generic/main.cc b/generic/main.cc
index c203f35c..ce1a51c7 100644
--- a/generic/main.cc
+++ b/generic/main.cc
@@ -32,7 +32,7 @@ class GenericCommandHandler : public CommandHandler
public:
GenericCommandHandler(int argc, char **argv);
virtual ~GenericCommandHandler(){};
- std::unique_ptr<Context> createContext() override;
+ std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override;
void setupArchContext(Context *ctx) override{};
void customBitstream(Context *ctx) override;
@@ -51,8 +51,14 @@ po::options_description GenericCommandHandler::getArchOptions()
void GenericCommandHandler::customBitstream(Context *ctx) {}
-std::unique_ptr<Context> GenericCommandHandler::createContext()
+std::unique_ptr<Context> GenericCommandHandler::createContext(std::unordered_map<std::string, Property> &values)
{
+ ArchArgs chipArgs;
+ if (values.find("arch.name") != values.end()) {
+ std::string arch_name = values["arch.name"].str;
+ if (arch_name != "generic")
+ log_error("Unsuported architecture '%s'.\n", arch_name.c_str());
+ }
return std::unique_ptr<Context>(new Context(chipArgs));
}
diff --git a/generic/pack.cc b/generic/pack.cc
index 26ae9182..558eca7c 100644
--- a/generic/pack.cc
+++ b/generic/pack.cc
@@ -282,6 +282,7 @@ bool Arch::pack()
pack_io(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
+ ctx->settings[ctx->id("pack")] = "1";
ctx->assignArchInfo();
log_info("Checksum: 0x%08x\n", ctx->checksum());
return true;
diff --git a/gui/base.qrc b/gui/base.qrc
index 644b16a6..85d1432a 100644
--- a/gui/base.qrc
+++ b/gui/base.qrc
@@ -22,6 +22,7 @@
<file>resources/route.png</file>
<file>resources/time_add.png</file>
<file>resources/open_json.png</file>
+ <file>resources/save_json.png</file>
<file>resources/py.png</file>
</qresource>
</RCC>
diff --git a/gui/basewindow.cc b/gui/basewindow.cc
index 346efb88..550a4b93 100644
--- a/gui/basewindow.cc
+++ b/gui/basewindow.cc
@@ -29,17 +29,17 @@
#include "designwidget.h"
#include "fpgaviewwidget.h"
#include "jsonparse.h"
+#include "jsonwrite.h"
#include "log.h"
#include "mainwindow.h"
-#include "project.h"
#include "pythontab.h"
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
NEXTPNR_NAMESPACE_BEGIN
-BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : QMainWindow(parent), chipArgs(args), ctx(std::move(context)), timing_driven(false)
+BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : QMainWindow(parent), handler(handler), ctx(std::move(context)), timing_driven(false)
{
initBasenameResource();
qRegisterMetaType<std::string>();
@@ -130,25 +130,6 @@ void BaseMainWindow::writeInfo(std::string text) { console->info(text); }
void BaseMainWindow::createMenusAndBars()
{
// File menu / project toolbar actions
- actionNew = new QAction("New", this);
- actionNew->setIcon(QIcon(":/icons/resources/new.png"));
- actionNew->setShortcuts(QKeySequence::New);
- actionNew->setStatusTip("New project file");
- connect(actionNew, &QAction::triggered, this, &BaseMainWindow::new_proj);
-
- actionOpen = new QAction("Open", this);
- actionOpen->setIcon(QIcon(":/icons/resources/open.png"));
- actionOpen->setShortcuts(QKeySequence::Open);
- actionOpen->setStatusTip("Open an existing project file");
- connect(actionOpen, &QAction::triggered, this, &BaseMainWindow::open_proj);
-
- actionSave = new QAction("Save", this);
- actionSave->setIcon(QIcon(":/icons/resources/save.png"));
- actionSave->setShortcuts(QKeySequence::Save);
- actionSave->setStatusTip("Save existing project to disk");
- actionSave->setEnabled(false);
- connect(actionSave, &QAction::triggered, this, &BaseMainWindow::save_proj);
-
QAction *actionExit = new QAction("Exit", this);
actionExit->setIcon(QIcon(":/icons/resources/exit.png"));
actionExit->setShortcuts(QKeySequence::Quit);
@@ -158,13 +139,26 @@ void BaseMainWindow::createMenusAndBars()
// Help menu actions
QAction *actionAbout = new QAction("About", this);
- // Design menu options
+ // Gile menu options
+ actionNew = new QAction("New", this);
+ actionNew->setIcon(QIcon(":/icons/resources/new.png"));
+ actionNew->setShortcuts(QKeySequence::New);
+ actionNew->setStatusTip("New project");
+ connect(actionNew, &QAction::triggered, this, &BaseMainWindow::new_proj);
+
actionLoadJSON = new QAction("Open JSON", this);
actionLoadJSON->setIcon(QIcon(":/icons/resources/open_json.png"));
actionLoadJSON->setStatusTip("Open an existing JSON file");
actionLoadJSON->setEnabled(true);
connect(actionLoadJSON, &QAction::triggered, this, &BaseMainWindow::open_json);
+ actionSaveJSON = new QAction("Save JSON", this);
+ actionSaveJSON->setIcon(QIcon(":/icons/resources/save_json.png"));
+ actionSaveJSON->setStatusTip("Write to JSON file");
+ actionSaveJSON->setEnabled(true);
+ connect(actionSaveJSON, &QAction::triggered, this, &BaseMainWindow::save_json);
+
+ // Design menu options
actionPack = new QAction("Pack", this);
actionPack->setIcon(QIcon(":/icons/resources/pack.png"));
actionPack->setStatusTip("Pack current design");
@@ -244,13 +238,12 @@ void BaseMainWindow::createMenusAndBars()
// Add File menu actions
menuFile->addAction(actionNew);
- menuFile->addAction(actionOpen);
- menuFile->addAction(actionSave);
+ menuFile->addAction(actionLoadJSON);
+ menuFile->addAction(actionSaveJSON);
menuFile->addSeparator();
menuFile->addAction(actionExit);
// Add Design menu actions
- menuDesign->addAction(actionLoadJSON);
menuDesign->addAction(actionPack);
menuDesign->addAction(actionAssignBudget);
menuDesign->addAction(actionPlace);
@@ -261,17 +254,13 @@ void BaseMainWindow::createMenusAndBars()
// Add Help menu actions
menuHelp->addAction(actionAbout);
- // Project toolbar
- QToolBar *projectToolBar = new QToolBar("Project");
- addToolBar(Qt::TopToolBarArea, projectToolBar);
- projectToolBar->addAction(actionNew);
- projectToolBar->addAction(actionOpen);
- projectToolBar->addAction(actionSave);
-
// Main action bar
mainActionBar = new QToolBar("Main");
addToolBar(Qt::TopToolBarArea, mainActionBar);
+ mainActionBar->addAction(actionNew);
mainActionBar->addAction(actionLoadJSON);
+ mainActionBar->addAction(actionSaveJSON);
+ mainActionBar->addSeparator();
mainActionBar->addAction(actionPack);
mainActionBar->addAction(actionAssignBudget);
mainActionBar->addAction(actionPlace);
@@ -304,25 +293,29 @@ void BaseMainWindow::createMenusAndBars()
setStatusBar(statusBar);
}
-void BaseMainWindow::load_json(std::string filename)
+void BaseMainWindow::open_json()
{
- disableActions();
- std::ifstream f(filename);
- if (parse_json_file(f, filename, ctx.get())) {
- log("Loading design successful.\n");
+ QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
+ if (!fileName.isEmpty()) {
+ disableActions();
+ ctx = handler->load_json(fileName.toStdString());
+ Q_EMIT contextChanged(ctx.get());
Q_EMIT updateTreeView();
- updateLoaded();
- } else {
- actionLoadJSON->setEnabled(true);
- log("Loading design failed.\n");
+ log("Loading design successful.\n");
+ updateActions();
}
}
-void BaseMainWindow::open_json()
+void BaseMainWindow::save_json()
{
- QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
+ QString fileName = QFileDialog::getSaveFileName(this, QString("Save JSON"), QString(), QString("*.json"));
if (!fileName.isEmpty()) {
- load_json(fileName.toStdString());
+ std::string fn = fileName.toStdString();
+ std::ofstream f(fn);
+ if (write_json_file(f, fn, ctx.get()))
+ log("Saving JSON successful.\n");
+ else
+ log("Saving JSON failed.\n");
}
}
@@ -332,9 +325,7 @@ void BaseMainWindow::pack_finished(bool status)
if (status) {
log("Packing design successful.\n");
Q_EMIT updateTreeView();
- actionPlace->setEnabled(true);
- actionAssignBudget->setEnabled(true);
- onPackFinished();
+ updateActions();
} else {
log("Packing design failed.\n");
}
@@ -345,8 +336,7 @@ void BaseMainWindow::budget_finish(bool status)
disableActions();
if (status) {
log("Assigning timing budget successful.\n");
- actionPlace->setEnabled(true);
- onBudgetFinished();
+ updateActions();
} else {
log("Assigning timing budget failed.\n");
}
@@ -358,8 +348,7 @@ void BaseMainWindow::place_finished(bool status)
if (status) {
log("Placing design successful.\n");
Q_EMIT updateTreeView();
- actionRoute->setEnabled(true);
- onPlaceFinished();
+ updateActions();
} else {
log("Placing design failed.\n");
}
@@ -370,7 +359,7 @@ void BaseMainWindow::route_finished(bool status)
if (status) {
log("Routing design successful.\n");
Q_EMIT updateTreeView();
- onRouteFinished();
+ updateActions();
} else
log("Routing design failed.\n");
}
@@ -386,9 +375,6 @@ void BaseMainWindow::taskStarted()
disableActions();
actionPause->setEnabled(true);
actionStop->setEnabled(true);
-
- actionNew->setEnabled(false);
- actionOpen->setEnabled(false);
}
void BaseMainWindow::taskPaused()
@@ -396,9 +382,6 @@ void BaseMainWindow::taskPaused()
disableActions();
actionPlay->setEnabled(true);
actionStop->setEnabled(true);
-
- actionNew->setEnabled(false);
- actionOpen->setEnabled(false);
}
void BaseMainWindow::budget()
@@ -416,52 +399,32 @@ void BaseMainWindow::place() { Q_EMIT task->place(timing_driven); }
void BaseMainWindow::disableActions()
{
- actionLoadJSON->setEnabled(false);
+ actionLoadJSON->setEnabled(true);
actionPack->setEnabled(false);
actionAssignBudget->setEnabled(false);
actionPlace->setEnabled(false);
actionRoute->setEnabled(false);
+
actionExecutePy->setEnabled(true);
actionPlay->setEnabled(false);
actionPause->setEnabled(false);
actionStop->setEnabled(false);
- actionNew->setEnabled(true);
- actionOpen->setEnabled(true);
-
- if (ctx->settings.find(ctx->id("input/json")) != ctx->settings.end())
- actionSave->setEnabled(true);
- else
- actionSave->setEnabled(false);
-
onDisableActions();
}
-void BaseMainWindow::updateLoaded()
-{
- disableActions();
- actionPack->setEnabled(true);
- onJsonLoaded();
- onProjectLoaded();
-}
-
-void BaseMainWindow::projectLoad(std::string filename)
+void BaseMainWindow::updateActions()
{
- ProjectHandler proj;
- disableActions();
- ctx = proj.load(filename);
- Q_EMIT contextChanged(ctx.get());
- log_info("Loaded project %s...\n", filename.c_str());
- updateLoaded();
-}
+ if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
+ actionPack->setEnabled(true);
+ else if (ctx->settings.find(ctx->id("place")) == ctx->settings.end()) {
+ actionAssignBudget->setEnabled(true);
+ actionPlace->setEnabled(true);
+ } else if (ctx->settings.find(ctx->id("route")) == ctx->settings.end())
+ actionRoute->setEnabled(true);
-void BaseMainWindow::open_proj()
-{
- QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj"));
- if (!fileName.isEmpty()) {
- projectLoad(fileName.toStdString());
- }
+ onUpdateActions();
}
void BaseMainWindow::execute_python()
@@ -473,18 +436,5 @@ void BaseMainWindow::execute_python()
}
void BaseMainWindow::notifyChangeContext() { Q_EMIT contextChanged(ctx.get()); }
-void BaseMainWindow::save_proj()
-{
- if (currentProj.empty()) {
- QString fileName = QFileDialog::getSaveFileName(this, QString("Save Project"), QString(), QString("*.proj"));
- if (fileName.isEmpty())
- return;
- currentProj = fileName.toStdString();
- }
- if (!currentProj.empty()) {
- ProjectHandler proj;
- proj.save(ctx.get(), currentProj);
- }
-}
NEXTPNR_NAMESPACE_END
diff --git a/gui/basewindow.h b/gui/basewindow.h
index 0b2d3fbc..305cb6c9 100644
--- a/gui/basewindow.h
+++ b/gui/basewindow.h
@@ -20,6 +20,7 @@
#ifndef BASEMAINWINDOW_H
#define BASEMAINWINDOW_H
+#include "command.h"
#include "nextpnr.h"
#include "worker.h"
@@ -45,25 +46,19 @@ class BaseMainWindow : public QMainWindow
Q_OBJECT
public:
- explicit BaseMainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit BaseMainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~BaseMainWindow();
Context *getContext() { return ctx.get(); }
- void updateLoaded();
- void projectLoad(std::string filename);
+ void updateActions();
+
void notifyChangeContext();
protected:
void createMenusAndBars();
void disableActions();
- void load_json(std::string filename);
virtual void onDisableActions(){};
- virtual void onJsonLoaded(){};
- virtual void onProjectLoaded(){};
- virtual void onPackFinished(){};
- virtual void onBudgetFinished(){};
- virtual void onPlaceFinished(){};
- virtual void onRouteFinished(){};
+ virtual void onUpdateActions(){};
protected Q_SLOTS:
void writeInfo(std::string text);
@@ -71,10 +66,8 @@ class BaseMainWindow : public QMainWindow
virtual void new_proj() = 0;
- void open_proj();
- void save_proj();
-
void open_json();
+ void save_json();
void budget();
void place();
@@ -95,7 +88,7 @@ class BaseMainWindow : public QMainWindow
protected:
// state variables
- ArchArgs chipArgs;
+ CommandHandler *handler;
std::unique_ptr<Context> ctx;
TaskManager *task;
bool timing_driven;
@@ -116,10 +109,9 @@ class BaseMainWindow : public QMainWindow
QProgressBar *progressBar;
QAction *actionNew;
- QAction *actionOpen;
- QAction *actionSave;
-
QAction *actionLoadJSON;
+ QAction *actionSaveJSON;
+
QAction *actionPack;
QAction *actionAssignBudget;
QAction *actionPlace;
diff --git a/gui/create_img.sh b/gui/create_img.sh
index 1508d023..2e7b1f87 100755
--- a/gui/create_img.sh
+++ b/gui/create_img.sh
@@ -2,5 +2,6 @@ convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 '
convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'PCF'" resources/open.png ice40/resources/open_pcf.png
convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'BASE'" resources/open.png ecp5/resources/open_base.png
convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'LPF'" resources/open.png ecp5/resources/open_lpf.png
-convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'ASC'" resources/save.png ice40/resources/save_asc.png
-convert -font helvetica -fill red -pointsize 7 -gravity center -draw "text 2,8 'CONFIG'" resources/save.png ecp5/resources/save_config.png \ No newline at end of file
+convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'ASC'" resources/save.png ice40/resources/save_asc.png
+convert -font helvetica -fill red -pointsize 7 -gravity center -draw "text 2,8 'CONFIG'" resources/save.png ecp5/resources/save_config.png
+convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'JSON'" resources/save.png resources/save_json.png
diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc
index c6c7bc97..880af047 100644
--- a/gui/ecp5/mainwindow.cc
+++ b/gui/ecp5/mainwindow.cc
@@ -30,8 +30,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : BaseMainWindow(std::move(context), args, parent)
+MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : BaseMainWindow(std::move(context), handler, parent)
{
initMainResource();
@@ -47,7 +47,7 @@ MainWindow::~MainWindow() {}
void MainWindow::newContext(Context *ctx)
{
- std::string title = "nextpnr-generic - " + ctx->getChipName() + " ( " + chipArgs.package + " )";
+ std::string title = "nextpnr-ecp5 - " + ctx->getChipName() + " ( " + ctx->archArgs().package + " )";
setWindowTitle(title.c_str());
}
@@ -60,12 +60,6 @@ void MainWindow::createMenu()
actionLoadLPF->setEnabled(false);
connect(actionLoadLPF, &QAction::triggered, this, &MainWindow::open_lpf);
- actionLoadBase = new QAction("Open Base Config", this);
- actionLoadBase->setIcon(QIcon(":/icons/resources/open_base.png"));
- actionLoadBase->setStatusTip("Open Base Config file");
- actionLoadBase->setEnabled(false);
- connect(actionLoadBase, &QAction::triggered, this, &MainWindow::open_base);
-
actionSaveConfig = new QAction("Save Bitstream", this);
actionSaveConfig->setIcon(QIcon(":/icons/resources/save_config.png"));
actionSaveConfig->setStatusTip("Save Bitstream config file");
@@ -75,12 +69,10 @@ void MainWindow::createMenu()
// Add actions in menus
mainActionBar->addSeparator();
mainActionBar->addAction(actionLoadLPF);
- mainActionBar->addAction(actionLoadBase);
mainActionBar->addAction(actionSaveConfig);
menuDesign->addSeparator();
menuDesign->addAction(actionLoadLPF);
- menuDesign->addAction(actionLoadBase);
menuDesign->addAction(actionSaveConfig);
}
@@ -121,7 +113,7 @@ void MainWindow::new_proj()
bool ok;
QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
if (ok && !item.isEmpty()) {
-
+ ArchArgs chipArgs;
chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(item);
QString package = QInputDialog::getItem(this, "Select package", "Package:", getSupportedPackages(chipArgs.type),
@@ -139,13 +131,6 @@ void MainWindow::new_proj()
}
}
-void MainWindow::load_base_config(std::string filename)
-{
- disableActions();
- currentBaseConfig = filename;
- actionSaveConfig->setEnabled(true);
-}
-
void MainWindow::open_lpf()
{
QString fileName = QFileDialog::getOpenFileName(this, QString("Open LPF"), QString(), QString("*.lpf"));
@@ -162,21 +147,13 @@ void MainWindow::open_lpf()
}
}
-void MainWindow::open_base()
-{
- QString fileName = QFileDialog::getOpenFileName(this, QString("Open Base Config"), QString(), QString("*.config"));
- if (!fileName.isEmpty()) {
- load_base_config(fileName.toStdString());
- }
-}
-
void MainWindow::save_config()
{
QString fileName = QFileDialog::getSaveFileName(this, QString("Save Bitstream"), QString(), QString("*.config"));
if (!fileName.isEmpty()) {
std::string fn = fileName.toStdString();
disableActions();
- write_bitstream(ctx.get(), currentBaseConfig, fileName.toStdString());
+ write_bitstream(ctx.get(), "", fileName.toStdString());
log("Saving Bitstream successful.\n");
}
}
@@ -184,18 +161,15 @@ void MainWindow::save_config()
void MainWindow::onDisableActions()
{
actionLoadLPF->setEnabled(false);
- actionLoadBase->setEnabled(false);
actionSaveConfig->setEnabled(false);
}
-void MainWindow::onJsonLoaded() { actionLoadLPF->setEnabled(true); }
-
-void MainWindow::onRouteFinished() { actionLoadBase->setEnabled(true); }
-
-void MainWindow::onProjectLoaded()
+void MainWindow::onUpdateActions()
{
- if (ctx->settings.find(ctx->id("input/lpf")) != ctx->settings.end())
- actionLoadLPF->setEnabled(false);
+ if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
+ actionLoadLPF->setEnabled(true);
+ if (ctx->settings.find(ctx->id("route")) != ctx->settings.end())
+ actionSaveConfig->setEnabled(true);
}
NEXTPNR_NAMESPACE_END
diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h
index 721c6c0b..e60fc5b0 100644
--- a/gui/ecp5/mainwindow.h
+++ b/gui/ecp5/mainwindow.h
@@ -29,34 +29,25 @@ class MainWindow : public BaseMainWindow
Q_OBJECT
public:
- explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
void createMenu();
- void load_base_config(std::string filename);
protected:
void onDisableActions() override;
- void onJsonLoaded() override;
- void onRouteFinished() override;
- void onProjectLoaded() override;
+ void onUpdateActions() override;
protected Q_SLOTS:
void new_proj() override;
void newContext(Context *ctx);
void open_lpf();
- void open_base();
void save_config();
private:
QAction *actionLoadLPF;
- QAction *actionLoadBase;
QAction *actionSaveConfig;
-
- ArchArgs chipArgs;
-
- std::string currentBaseConfig;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc
index 54d1f2c8..f616ca1a 100644
--- a/gui/generic/mainwindow.cc
+++ b/gui/generic/mainwindow.cc
@@ -26,9 +26,10 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : BaseMainWindow(std::move(context), args, parent)
+MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : BaseMainWindow(std::move(context), handler, parent)
{
+ initMainResource();
QMessageBox::critical(0, "Error - FIXME", "No GUI support for nextpnr-generic");
std::exit(1);
}
diff --git a/gui/generic/mainwindow.h b/gui/generic/mainwindow.h
index bb6a4cf1..4d1cf598 100644
--- a/gui/generic/mainwindow.h
+++ b/gui/generic/mainwindow.h
@@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow
Q_OBJECT
public:
- explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc
index f270b112..ccff2117 100644
--- a/gui/ice40/mainwindow.cc
+++ b/gui/ice40/mainwindow.cc
@@ -35,8 +35,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : BaseMainWindow(std::move(context), args, parent)
+MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : BaseMainWindow(std::move(context), handler, parent)
{
initMainResource();
@@ -123,7 +123,7 @@ void MainWindow::new_proj()
bool ok;
QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
if (ok && !item.isEmpty()) {
-
+ ArchArgs chipArgs;
chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(item);
QString package = QInputDialog::getItem(this, "Select package", "Package:", getSupportedPackages(chipArgs.type),
@@ -156,7 +156,7 @@ void MainWindow::load_pcf(std::string filename)
void MainWindow::newContext(Context *ctx)
{
- std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + chipArgs.package + " )";
+ std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + ctx->archArgs().package + " )";
setWindowTitle(title.c_str());
}
@@ -186,13 +186,12 @@ void MainWindow::onDisableActions()
actionSaveAsc->setEnabled(false);
}
-void MainWindow::onJsonLoaded() { actionLoadPCF->setEnabled(true); }
-void MainWindow::onRouteFinished() { actionSaveAsc->setEnabled(true); }
-
-void MainWindow::onProjectLoaded()
+void MainWindow::onUpdateActions()
{
- if (ctx->settings.find(ctx->id("input/pcf")) != ctx->settings.end())
- actionLoadPCF->setEnabled(false);
+ if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
+ actionLoadPCF->setEnabled(true);
+ if (ctx->settings.find(ctx->id("route")) != ctx->settings.end())
+ actionSaveAsc->setEnabled(true);
}
NEXTPNR_NAMESPACE_END
diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h
index 4a9a7d8e..6e725a16 100644
--- a/gui/ice40/mainwindow.h
+++ b/gui/ice40/mainwindow.h
@@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow
Q_OBJECT
public:
- explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
@@ -39,9 +39,7 @@ class MainWindow : public BaseMainWindow
void load_pcf(std::string filename);
void onDisableActions() override;
- void onJsonLoaded() override;
- void onRouteFinished() override;
- void onProjectLoaded() override;
+ void onUpdateActions() override;
protected Q_SLOTS:
void new_proj() override;
diff --git a/gui/resources/save_json.png b/gui/resources/save_json.png
new file mode 100644
index 00000000..cbeb3243
--- /dev/null
+++ b/gui/resources/save_json.png
Binary files differ
diff --git a/gui/worker.cc b/gui/worker.cc
index 900883d4..bd14771b 100644
--- a/gui/worker.cc
+++ b/gui/worker.cc
@@ -68,7 +68,7 @@ void Worker::budget(double freq)
{
Q_EMIT taskStarted();
try {
- ctx->target_freq = freq;
+ ctx->settings[ctx->id("target_freq")] = std::to_string(freq);
assign_budget(ctx);
Q_EMIT budget_finish(true);
} catch (WorkerInterruptionRequested) {
@@ -80,7 +80,7 @@ void Worker::place(bool timing_driven)
{
Q_EMIT taskStarted();
try {
- ctx->timing_driven = timing_driven;
+ ctx->settings[ctx->id("timing_driven")] = std::to_string(timing_driven);
Q_EMIT place_finished(ctx->place());
} catch (WorkerInterruptionRequested) {
Q_EMIT taskCanceled();
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 80e1fb4c..0b1d280c 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -683,16 +683,24 @@ bool Arch::place()
} else {
log_error("iCE40 architecture does not support placer '%s'\n", placer.c_str());
}
+ bool retVal = true;
if (bool_or_default(settings, id("opt_timing"), false)) {
TimingOptCfg tocfg(getCtx());
tocfg.cellTypes.insert(id_ICESTORM_LC);
- return timing_opt(getCtx(), tocfg);
- } else {
- return true;
+ retVal = timing_opt(getCtx(), tocfg);
}
+ getCtx()->settings[getCtx()->id("place")] = "1";
+ archInfoToAttributes();
+ return retVal;
}
-bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
+bool Arch::route()
+{
+ bool retVal = router1(getCtx(), Router1Cfg(getCtx()));
+ getCtx()->settings[getCtx()->id("route")] = "1";
+ archInfoToAttributes();
+ return retVal;
+}
// -----------------------------------------------------------------------
diff --git a/ice40/main.cc b/ice40/main.cc
index 9b79a08c..83cb04b0 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -36,7 +36,7 @@ class Ice40CommandHandler : public CommandHandler
public:
Ice40CommandHandler(int argc, char **argv);
virtual ~Ice40CommandHandler(){};
- std::unique_ptr<Context> createContext() override;
+ std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override;
void setupArchContext(Context *ctx) override;
void validate() override;
void customAfterLoad(Context *ctx) override;
@@ -116,8 +116,10 @@ void Ice40CommandHandler::setupArchContext(Context *ctx)
}
}
-std::unique_ptr<Context> Ice40CommandHandler::createContext()
+std::unique_ptr<Context> Ice40CommandHandler::createContext(std::unordered_map<std::string, Property> &values)
{
+ ArchArgs chipArgs;
+ chipArgs.type = ArchArgs::NONE;
if (vm.count("lp384")) {
chipArgs.type = ArchArgs::LP384;
chipArgs.package = "qn32";
@@ -153,6 +155,49 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext()
chipArgs.package = "sg48";
}
+ if (vm.count("package"))
+ chipArgs.package = vm["package"].as<std::string>();
+
+ if (values.find("arch.name") != values.end()) {
+ std::string arch_name = values["arch.name"].str;
+ if (arch_name != "ice40")
+ log_error("Unsuported architecture '%s'.\n", arch_name.c_str());
+ }
+ if (values.find("arch.type") != values.end()) {
+ std::string arch_type = values["arch.type"].str;
+ if (chipArgs.type != ArchArgs::NONE)
+ log_error("Overriding architecture is unsuported.\n");
+
+ if (arch_type == "lp384") {
+ chipArgs.type = ArchArgs::LP384;
+ }
+ if (arch_type == "lp1k") {
+ chipArgs.type = ArchArgs::LP1K;
+ }
+ if (arch_type == "lp8k") {
+ chipArgs.type = ArchArgs::LP8K;
+ }
+ if (arch_type == "hx1k") {
+ chipArgs.type = ArchArgs::HX1K;
+ }
+ if (arch_type == "hx8k") {
+ chipArgs.type = ArchArgs::HX8K;
+ }
+ if (arch_type == "up5k") {
+ chipArgs.type = ArchArgs::UP5K;
+ }
+ if (arch_type == "u4k") {
+ chipArgs.type = ArchArgs::U4K;
+ }
+ if (chipArgs.type == ArchArgs::NONE)
+ log_error("Unsuported FPGA type '%s'.\n", arch_type.c_str());
+ }
+ if (values.find("arch.package") != values.end()) {
+ if (vm.count("package"))
+ log_error("Overriding architecture is unsuported.\n");
+ chipArgs.package = values["arch.package"].str;
+ }
+
if (chipArgs.type == ArchArgs::NONE) {
chipArgs.type = ArchArgs::HX1K;
chipArgs.package = "tq144";
@@ -163,11 +208,11 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext()
}
#endif
- if (vm.count("package"))
- chipArgs.package = vm["package"].as<std::string>();
-
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
+ for (auto &val : values)
+ ctx->settings[ctx->id(val.first)] = val.second;
+ ctx->settings[ctx->id("arch.package")] = ctx->archArgs().package;
if (vm.count("promote-logic"))
ctx->settings[ctx->id("promote_logic")] = "1";
if (vm.count("no-promote-globals"))
diff --git a/ice40/pack.cc b/ice40/pack.cc
index f520b295..d1366c9c 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -341,6 +341,11 @@ static void pack_constants(Context *ctx)
gnd_net->driver.port = ctx->id("O");
gnd_cell->ports.at(ctx->id("O")).net = gnd_net.get();
+ NetInfo *gnd_net_info = gnd_net.get();
+ if (ctx->nets.find(ctx->id("$PACKER_GND_NET")) != ctx->nets.end()) {
+ gnd_net_info = ctx->nets.find(ctx->id("$PACKER_GND_NET"))->second.get();
+ }
+
std::unique_ptr<CellInfo> vcc_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_VCC");
vcc_cell->params[ctx->id("LUT_INIT")] = "1";
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
@@ -349,6 +354,11 @@ static void pack_constants(Context *ctx)
vcc_net->driver.port = ctx->id("O");
vcc_cell->ports.at(ctx->id("O")).net = vcc_net.get();
+ NetInfo *vcc_net_info = vcc_net.get();
+ if (ctx->nets.find(ctx->id("$PACKER_VCC_NET")) != ctx->nets.end()) {
+ vcc_net_info = ctx->nets.find(ctx->id("$PACKER_VCC_NET"))->second.get();
+ }
+
std::vector<IdString> dead_nets;
bool gnd_used = false;
@@ -357,26 +367,28 @@ static void pack_constants(Context *ctx)
NetInfo *ni = net.second;
if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
IdString drv_cell = ni->driver.cell->name;
- set_net_constant(ctx, ni, gnd_net.get(), false);
+ set_net_constant(ctx, ni, gnd_net_info, false);
gnd_used = true;
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
} else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
IdString drv_cell = ni->driver.cell->name;
- set_net_constant(ctx, ni, vcc_net.get(), true);
+ set_net_constant(ctx, ni, vcc_net_info, true);
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
}
}
- if (gnd_used) {
+ if (gnd_used && (gnd_net_info == gnd_net.get())) {
ctx->cells[gnd_cell->name] = std::move(gnd_cell);
ctx->nets[gnd_net->name] = std::move(gnd_net);
}
// Vcc cell always inserted for now, as it may be needed during carry legalisation (TODO: trim later if actually
// never used?)
- ctx->cells[vcc_cell->name] = std::move(vcc_cell);
- ctx->nets[vcc_net->name] = std::move(vcc_net);
+ if (vcc_net_info == vcc_net.get()) {
+ ctx->cells[vcc_cell->name] = std::move(vcc_cell);
+ ctx->nets[vcc_net->name] = std::move(vcc_net);
+ }
for (auto dn : dead_nets) {
ctx->nets.erase(dn);
@@ -1224,13 +1236,14 @@ static void pack_special(Context *ctx)
}
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
- std::string fbp_value = feedback_path == "DELAY"
- ? "0"
- : feedback_path == "SIMPLE"
- ? "1"
- : feedback_path == "PHASE_AND_DELAY"
- ? "2"
- : feedback_path == "EXTERNAL" ? "6" : feedback_path;
+ std::string fbp_value =
+ feedback_path == "DELAY"
+ ? "0"
+ : feedback_path == "SIMPLE"
+ ? "1"
+ : feedback_path == "PHASE_AND_DELAY"
+ ? "2"
+ : feedback_path == "EXTERNAL" ? "6" : std::string(feedback_path);
if (!std::all_of(fbp_value.begin(), fbp_value.end(), isdigit))
log_error("PLL '%s' has unsupported FEEDBACK_PATH value '%s'\n", ci->name.c_str(ctx),
feedback_path.c_str());
@@ -1435,6 +1448,8 @@ bool Arch::pack()
ctx->assignArchInfo();
constrain_chains(ctx);
ctx->assignArchInfo();
+ ctx->settings[ctx->id("pack")] = "1";
+ archInfoToAttributes();
log_info("Checksum: 0x%08x\n", ctx->checksum());
return true;
} catch (log_execution_error_exception) {
diff --git a/ice40/pcf.cc b/ice40/pcf.cc
index 15132800..a854a780 100644
--- a/ice40/pcf.cc
+++ b/ice40/pcf.cc
@@ -119,7 +119,7 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
}
}
}
- ctx->settings.emplace(ctx->id("input/pcf"), filename);
+ ctx->settings[ctx->id("input/pcf")] = filename;
return true;
} catch (log_execution_error_exception) {
return false;
diff --git a/ice40/project.cc b/ice40/project.cc
deleted file mode 100644
index bbd82fd7..00000000
--- a/ice40/project.cc
+++ /dev/null
@@ -1,76 +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 <boost/filesystem/convenience.hpp>
-#include <fstream>
-#include "log.h"
-#include "pcf.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
-{
- root.put("project.arch.package", ctx->archArgs().package);
- if (ctx->settings.find(ctx->id("input/pcf")) != ctx->settings.end()) {
- std::string fn = ctx->settings[ctx->id("input/pcf")];
- root.put("project.input.pcf", make_relative(fn, path).string());
- }
-}
-
-std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
-{
- ArchArgs chipArgs;
- std::string arch_type = root.get<std::string>("project.arch.type");
- if (arch_type == "lp384") {
- chipArgs.type = ArchArgs::LP384;
- }
- if (arch_type == "lp1k") {
- chipArgs.type = ArchArgs::LP1K;
- }
- if (arch_type == "lp8k") {
- chipArgs.type = ArchArgs::LP8K;
- }
- if (arch_type == "hx1k") {
- chipArgs.type = ArchArgs::HX1K;
- }
- if (arch_type == "hx8k") {
- chipArgs.type = ArchArgs::HX8K;
- }
- if (arch_type == "up5k") {
- chipArgs.type = ArchArgs::UP5K;
- }
- if (arch_type == "u4k") {
- chipArgs.type = ArchArgs::U4K;
- }
- chipArgs.package = root.get<std::string>("project.arch.package");
-
- return std::unique_ptr<Context>(new Context(chipArgs));
-}
-
-void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path)
-{
- auto input = root.get_child("project").get_child("input");
- boost::filesystem::path pcf = boost::filesystem::path(path) / input.get<std::string>("pcf");
- std::ifstream f(pcf.string());
- if (!apply_pcf(ctx, input.get<std::string>("pcf"), f))
- log_error("Loading PCF failed.\n");
-}
-
-NEXTPNR_NAMESPACE_END
diff --git a/json/jsonparse.cc b/json/jsonparse.cc
index d463d8ce..caedbb5b 100644
--- a/json/jsonparse.cc
+++ b/json/jsonparse.cc
@@ -314,7 +314,7 @@ bool is_blackbox(JsonNode *node)
}
void json_import_cell_params(Context *ctx, string &modname, CellInfo *cell, JsonNode *param_node,
- std::unordered_map<IdString, std::string> *dest, int param_id)
+ std::unordered_map<IdString, Property> *dest, int param_id)
{
//
JsonNode *param;
@@ -324,9 +324,9 @@ void json_import_cell_params(Context *ctx, string &modname, CellInfo *cell, Json
pId = ctx->id(param_node->data_dict_keys[param_id]);
if (param->type == 'N') {
- (*dest)[pId] = std::to_string(param->data_number);
+ (*dest)[pId].setNumber(param->data_number);
} else if (param->type == 'S')
- (*dest)[pId] = param->data_string;
+ (*dest)[pId].setString(param->data_string);
else
log_error("JSON parameter type of \"%s\' of cell \'%s\' not supported\n", pId.c_str(ctx),
cell->name.c_str(ctx));
@@ -337,6 +337,49 @@ void json_import_cell_params(Context *ctx, string &modname, CellInfo *cell, Json
pId.c_str(ctx), cell->params[pId].c_str(), cell->name.c_str(ctx), modname.c_str());
}
+void json_import_net_attrib(Context *ctx, string &modname, NetInfo *net, JsonNode *param_node,
+ std::unordered_map<IdString, Property> *dest, int param_id)
+{
+ //
+ JsonNode *param;
+ IdString pId;
+ //
+ param = param_node->data_dict.at(param_node->data_dict_keys[param_id]);
+
+ pId = ctx->id(param_node->data_dict_keys[param_id]);
+ if (param->type == 'N') {
+ (*dest)[pId].setNumber(param->data_number);
+ } else if (param->type == 'S')
+ (*dest)[pId].setString(param->data_string);
+ else
+ log_error("JSON parameter type of \"%s\' of net \'%s\' not supported\n", pId.c_str(ctx), net->name.c_str(ctx));
+ if (json_debug)
+ log_info(" Added parameter \'%s\'=%s to net \'%s\' "
+ "of module \'%s\'\n",
+ pId.c_str(ctx), net->attrs[pId].c_str(), net->name.c_str(ctx), modname.c_str());
+}
+
+void json_import_top_attrib(Context *ctx, string &modname, JsonNode *param_node,
+ std::unordered_map<IdString, Property> *dest, int param_id)
+{
+ //
+ JsonNode *param;
+ IdString pId;
+ //
+ param = param_node->data_dict.at(param_node->data_dict_keys[param_id]);
+
+ pId = ctx->id(param_node->data_dict_keys[param_id]);
+ if (param->type == 'N') {
+ (*dest)[pId].setNumber(param->data_number);
+ } else if (param->type == 'S')
+ (*dest)[pId].setString(param->data_string);
+ else
+ log_error("JSON parameter type of \"%s\' of module not supported\n", pId.c_str(ctx));
+ if (json_debug)
+ log_info(" Added parameter \'%s\'=%s module \'%s\'\n", pId.c_str(ctx), (*dest)[pId].c_str(),
+ modname.c_str());
+}
+
static int const_net_idx = 0;
template <typename F>
@@ -625,62 +668,70 @@ static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, const string
// During packing, this generic IO buffer will be converted to an
// architecure primitive.
//
- std::unique_ptr<CellInfo> iobuf = std::unique_ptr<CellInfo>(new CellInfo());
- iobuf->name = ctx->id(name);
- std::copy(net->attrs.begin(), net->attrs.end(), std::inserter(iobuf->attrs, iobuf->attrs.begin()));
- if (type == PORT_IN) {
- if (ctx->verbose)
- log_info("processing input port %s\n", name.c_str());
- iobuf->type = ctx->id("$nextpnr_ibuf");
- iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT};
- // Special case: input, etc, directly drives inout
- if (net->driver.cell != nullptr) {
- if (net->driver.cell->type != ctx->id("$nextpnr_iobuf"))
- log_error("Top-level input '%s' also driven by %s.%s.\n", name.c_str(),
- net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx));
- net = net->driver.cell->ports.at(ctx->id("I")).net;
- }
- assert(net->driver.cell == nullptr);
- net->driver.port = ctx->id("O");
- net->driver.cell = iobuf.get();
- } else if (type == PORT_OUT) {
- if (ctx->verbose)
- log_info("processing output port %s\n", name.c_str());
- iobuf->type = ctx->id("$nextpnr_obuf");
- iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), net, PORT_IN};
- PortRef ref;
- ref.cell = iobuf.get();
- ref.port = ctx->id("I");
- net->users.push_back(ref);
- } else if (type == PORT_INOUT) {
- if (ctx->verbose)
- log_info("processing inout port %s\n", name.c_str());
- iobuf->type = ctx->id("$nextpnr_iobuf");
- iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), nullptr, PORT_IN};
-
- // Split the input and output nets for bidir ports
- std::unique_ptr<NetInfo> net2 = std::unique_ptr<NetInfo>(new NetInfo());
- net2->name = ctx->id("$" + net->name.str(ctx) + "$iobuf_i");
- net2->driver = net->driver;
- if (net->driver.cell != nullptr) {
- net2->driver.cell->ports[net2->driver.port].net = net2.get();
- net->driver.cell = nullptr;
+ if (ctx->settings.find(ctx->id("synth")) == ctx->settings.end()) {
+ std::unique_ptr<CellInfo> iobuf = std::unique_ptr<CellInfo>(new CellInfo());
+ iobuf->name = ctx->id(name);
+ std::copy(net->attrs.begin(), net->attrs.end(), std::inserter(iobuf->attrs, iobuf->attrs.begin()));
+ if (type == PORT_IN) {
+ if (ctx->verbose)
+ log_info("processing input port %s\n", name.c_str());
+ iobuf->type = ctx->id("$nextpnr_ibuf");
+ iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT};
+ // Special case: input, etc, directly drives inout
+ if (net->driver.cell != nullptr) {
+ if (net->driver.cell->type != ctx->id("$nextpnr_iobuf"))
+ log_error("Top-level input '%s' also driven by %s.%s.\n", name.c_str(),
+ net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx));
+ net = net->driver.cell->ports.at(ctx->id("I")).net;
+ }
+ assert(net->driver.cell == nullptr);
+ net->driver.port = ctx->id("O");
+ net->driver.cell = iobuf.get();
+ } else if (type == PORT_OUT) {
+ if (ctx->verbose)
+ log_info("processing output port %s\n", name.c_str());
+ iobuf->type = ctx->id("$nextpnr_obuf");
+ iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), net, PORT_IN};
+ PortRef ref;
+ ref.cell = iobuf.get();
+ ref.port = ctx->id("I");
+ net->users.push_back(ref);
+ } else if (type == PORT_INOUT) {
+ if (ctx->verbose)
+ log_info("processing inout port %s\n", name.c_str());
+ iobuf->type = ctx->id("$nextpnr_iobuf");
+ iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), nullptr, PORT_IN};
+
+ // Split the input and output nets for bidir ports
+ std::unique_ptr<NetInfo> net2 = std::unique_ptr<NetInfo>(new NetInfo());
+ net2->name = ctx->id("$" + net->name.str(ctx) + "$iobuf_i");
+ net2->driver = net->driver;
+ if (net->driver.cell != nullptr) {
+ net2->driver.cell->ports[net2->driver.port].net = net2.get();
+ net->driver.cell = nullptr;
+ }
+ iobuf->ports[ctx->id("I")].net = net2.get();
+ PortRef ref;
+ ref.cell = iobuf.get();
+ ref.port = ctx->id("I");
+ net2->users.push_back(ref);
+ ctx->nets[net2->name] = std::move(net2);
+
+ iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT};
+ assert(net->driver.cell == nullptr);
+ net->driver.port = ctx->id("O");
+ net->driver.cell = iobuf.get();
+ } else {
+ assert(false);
}
- iobuf->ports[ctx->id("I")].net = net2.get();
- PortRef ref;
- ref.cell = iobuf.get();
- ref.port = ctx->id("I");
- net2->users.push_back(ref);
- ctx->nets[net2->name] = std::move(net2);
-
- iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT};
- assert(net->driver.cell == nullptr);
- net->driver.port = ctx->id("O");
- net->driver.cell = iobuf.get();
- } else {
- assert(false);
+ ctx->cells[iobuf->name] = std::move(iobuf);
}
- ctx->cells[iobuf->name] = std::move(iobuf);
+
+ PortInfo pinfo;
+ pinfo.name = net->name;
+ pinfo.net = net;
+ pinfo.type = type;
+ ctx->ports[net->name] = pinfo;
}
void json_import_toplevel_port(Context *ctx, const string &modname, const std::vector<IdString> &netnames,
@@ -711,6 +762,11 @@ void json_import(Context *ctx, string modname, JsonNode *node)
return;
log_info("Importing module %s\n", modname.c_str());
+ ctx->attrs[ctx->id("module")] = modname;
+ JsonNode *attr_node = node->data_dict.at("attributes");
+ for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) {
+ json_import_top_attrib(ctx, modname, attr_node, &ctx->attrs, attrid);
+ }
JsonNode *ports_parent = nullptr;
if (node->data_dict.count("ports") > 0)
@@ -825,7 +881,43 @@ void json_import(Context *ctx, string modname, JsonNode *node)
json_import_toplevel_port(ctx, modname, netids, ports_parent->data_dict_keys[portid], here);
}
}
+ if (node->data_dict.count("netnames")) {
+ JsonNode *net_parent = node->data_dict.at("netnames");
+ for (int nnid = 0; nnid < GetSize(net_parent->data_dict_keys); nnid++) {
+ JsonNode *here;
+
+ here = net_parent->data_dict.at(net_parent->data_dict_keys[nnid]);
+ std::string basename = net_parent->data_dict_keys[nnid];
+ if (here->data_dict.count("bits")) {
+ JsonNode *bits = here->data_dict.at("bits");
+ assert(bits->type == 'A');
+ size_t num_bits = bits->data_array.size();
+ for (size_t i = 0; i < num_bits; i++) {
+ std::string name =
+ basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(i) + std::string("]"));
+ IdString net_id = ctx->id(name);
+ if (here->data_dict.count("attributes") && ctx->nets.find(net_id) != ctx->nets.end()) {
+ NetInfo *this_net = ctx->nets[net_id].get();
+
+ JsonNode *attr_node = here->data_dict.at("attributes");
+ if (attr_node->type != 'D')
+ log_error("JSON attribute list of \'%s\' is not a data dictionary\n",
+ this_net->name.c_str(ctx));
+
+ //
+ // Loop through all attributes, adding them into the
+ // design to annotate the cell
+ //
+ for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) {
+ json_import_net_attrib(ctx, modname, this_net, attr_node, &this_net->attrs, attrid);
+ }
+ }
+ }
+ }
+ }
+ }
check_all_nets_driven(ctx);
+ ctx->settings[ctx->id("synth")] = "1";
}
}; // End Namespace JsonParser
@@ -856,7 +948,55 @@ bool parse_json_file(std::istream &f, std::string &filename, Context *ctx)
log_info("Checksum: 0x%08x\n", ctx->checksum());
log_break();
- ctx->settings.emplace(ctx->id("input/json"), filename);
+ ctx->attributesToArchInfo();
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+
+bool load_json_settings(std::istream &f, std::string &filename, std::unordered_map<std::string, Property> &values)
+{
+ try {
+ using namespace JsonParser;
+
+ if (!f)
+ log_error("failed to open JSON file.\n");
+
+ int lineno = 1;
+
+ JsonNode root(f, lineno);
+
+ if (root.type != 'D')
+ log_error("JSON root node is not a dictionary.\n");
+
+ if (root.data_dict.count("modules") != 0) {
+ JsonNode *modules = root.data_dict.at("modules");
+
+ if (modules->type != 'D')
+ log_error("JSON modules node is not a dictionary.\n");
+
+ for (auto &it : modules->data_dict) {
+ JsonNode *node = it.second;
+ if (is_blackbox(node))
+ continue;
+
+ if (node->data_dict.count("settings")) {
+ JsonNode *attr_node = node->data_dict.at("settings");
+ for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) {
+ JsonNode *param = attr_node->data_dict.at(attr_node->data_dict_keys[attrid]);
+ std::string pId = attr_node->data_dict_keys[attrid];
+ if (param->type == 'N') {
+ values[pId].setNumber(param->data_number);
+ } else if (param->type == 'S')
+ values[pId].setString(param->data_string);
+ else
+ log_error("JSON parameter type of \"%s\' of module not supported\n", pId.c_str());
+ }
+ }
+ }
+ }
+
return true;
} catch (log_execution_error_exception) {
return false;
diff --git a/json/jsonparse.h b/json/jsonparse.h
index fe71444f..65e3f02e 100644
--- a/json/jsonparse.h
+++ b/json/jsonparse.h
@@ -27,7 +27,8 @@
NEXTPNR_NAMESPACE_BEGIN
extern bool parse_json_file(std::istream &, std::string &, Context *);
-
+extern bool load_json_settings(std::istream &f, std::string &filename,
+ std::unordered_map<std::string, Property> &values);
NEXTPNR_NAMESPACE_END
#endif
diff --git a/json/jsonwrite.cc b/json/jsonwrite.cc
new file mode 100644
index 00000000..0b7a5b25
--- /dev/null
+++ b/json/jsonwrite.cc
@@ -0,0 +1,180 @@
+/*
+ * 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 "jsonwrite.h"
+#include <assert.h>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <log.h>
+#include <map>
+#include <string>
+#include "nextpnr.h"
+#include "version.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+namespace JsonWriter {
+
+std::string get_string(std::string str)
+{
+ std::string newstr = "\"";
+ for (char c : str) {
+ if (c == '\\')
+ newstr += c;
+ newstr += c;
+ }
+ return newstr + "\"";
+}
+
+std::string get_name(IdString name, Context *ctx) { return get_string(name.c_str(ctx)); }
+
+void write_parameters(std::ostream &f, Context *ctx, const std::unordered_map<IdString, Property> &parameters,
+ bool for_module = false)
+{
+ bool first = true;
+ for (auto &param : parameters) {
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first, ctx).c_str());
+ if (param.second.isString())
+ f << get_string(param.second);
+ else
+ f << param.second.num;
+ first = false;
+ }
+}
+
+void write_module(std::ostream &f, Context *ctx)
+{
+ auto val = ctx->attrs.find(ctx->id("module"));
+ if (val != ctx->attrs.end())
+ f << stringf(" %s: {\n", get_string(val->second.str).c_str());
+ else
+ f << stringf(" %s: {\n", get_string("top").c_str());
+ f << stringf(" \"settings\": {");
+ write_parameters(f, ctx, ctx->settings, true);
+ f << stringf("\n },\n");
+ f << stringf(" \"attributes\": {");
+ write_parameters(f, ctx, ctx->attrs, true);
+ f << stringf("\n },\n");
+ f << stringf(" \"ports\": {");
+ bool first = true;
+ for (auto &pair : ctx->ports) {
+ auto &c = pair.second;
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s: {\n", get_name(c.name, ctx).c_str());
+ f << stringf(" \"direction\": \"%s\",\n",
+ c.type == PORT_IN ? "input" : c.type == PORT_INOUT ? "inout" : "output");
+ f << stringf(" \"bits\": [ %d ]\n", pair.first.index);
+ f << stringf(" }");
+ first = false;
+ }
+ f << stringf("\n },\n");
+
+ f << stringf(" \"cells\": {");
+ first = true;
+ for (auto &pair : ctx->cells) {
+ auto &c = pair.second;
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s: {\n", get_name(c->name, ctx).c_str());
+ f << stringf(" \"hide_name\": %s,\n", c->name.c_str(ctx)[0] == '$' ? "1" : "0");
+ f << stringf(" \"type\": %s,\n", get_name(c->type, ctx).c_str());
+ f << stringf(" \"parameters\": {");
+ write_parameters(f, ctx, c->params);
+ f << stringf("\n },\n");
+ f << stringf(" \"attributes\": {");
+ write_parameters(f, ctx, c->attrs);
+ f << stringf("\n },\n");
+ f << stringf(" \"port_directions\": {");
+ bool first2 = true;
+ for (auto &conn : c->ports) {
+ auto &p = conn.second;
+ std::string direction = (p.type == PORT_IN) ? "input" : (p.type == PORT_OUT) ? "output" : "inout";
+ f << stringf("%s\n", first2 ? "" : ",");
+ f << stringf(" %s: \"%s\"", get_name(conn.first, ctx).c_str(), direction.c_str());
+ first2 = false;
+ }
+ f << stringf("\n },\n");
+ f << stringf(" \"connections\": {");
+ first2 = true;
+ for (auto &conn : c->ports) {
+ auto &p = conn.second;
+ f << stringf("%s\n", first2 ? "" : ",");
+ if (p.net)
+ f << stringf(" %s: [ %d ]", get_name(conn.first, ctx).c_str(), p.net->name.index);
+ else
+ f << stringf(" %s: [ ]", get_name(conn.first, ctx).c_str());
+
+ first2 = false;
+ }
+ f << stringf("\n }\n");
+
+ f << stringf(" }");
+ first = false;
+ }
+
+ f << stringf("\n },\n");
+
+ f << stringf(" \"netnames\": {");
+ first = true;
+ for (auto &pair : ctx->nets) {
+ auto &w = pair.second;
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s: {\n", get_name(w->name, ctx).c_str());
+ f << stringf(" \"hide_name\": %s,\n", w->name.c_str(ctx)[0] == '$' ? "1" : "0");
+ f << stringf(" \"bits\": [ %d ] ,\n", pair.first.index);
+ f << stringf(" \"attributes\": {");
+ write_parameters(f, ctx, w->attrs);
+ f << stringf("\n }\n");
+ f << stringf(" }");
+ first = false;
+ }
+
+ f << stringf("\n }\n");
+ f << stringf(" }");
+}
+
+void write_context(std::ostream &f, Context *ctx)
+{
+ f << stringf("{\n");
+ f << stringf(" \"creator\": %s,\n",
+ get_string("Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")").c_str());
+ f << stringf(" \"modules\": {\n");
+ write_module(f, ctx);
+ f << stringf("\n }");
+ f << stringf("\n}\n");
+}
+
+}; // End Namespace JsonWriter
+
+bool write_json_file(std::ostream &f, std::string &filename, Context *ctx)
+{
+ try {
+ using namespace JsonWriter;
+ if (!f)
+ log_error("failed to open JSON file.\n");
+ write_context(f, ctx);
+ log_break();
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/generic/project.cc b/json/jsonwrite.h
index 342fcac3..9240bc96 100644
--- a/generic/project.cc
+++ b/json/jsonwrite.h
@@ -17,21 +17,17 @@
*
*/
-#include "project.h"
-#include <boost/filesystem/convenience.hpp>
-#include <fstream>
-#include "log.h"
+#ifndef JSON_WRITER
+#define JSON_WRITER
-NEXTPNR_NAMESPACE_BEGIN
-
-void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) {}
+#include <ostream>
+#include <string>
+#include "nextpnr.h"
-std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
-{
- ArchArgs chipArgs;
- return std::unique_ptr<Context>(new Context(chipArgs));
-}
+NEXTPNR_NAMESPACE_BEGIN
-void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {}
+extern bool write_json_file(std::ostream &, std::string &, Context *);
NEXTPNR_NAMESPACE_END
+
+#endif