Merge pull request #284 from YosysHQ/json_write
Initial support for writing to json files from nextpnr.
This commit is contained in:
commit
8f2813279c
@ -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")) {
|
||||
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");
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
@ -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
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
@ -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());
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
100
ecp5/main.cc
100
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,14 +138,7 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
|
||||
chipArgs.type = ArchArgs::LFE5UM5G_85F;
|
||||
if (vm.count("package"))
|
||||
chipArgs.package = vm["package"].as<std::string>();
|
||||
else
|
||||
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)
|
||||
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) {
|
||||
@ -146,10 +155,79 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
|
||||
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 (chipArgs.speed != ArchArgs::SPEED_8)
|
||||
log_error("Only speed grade 8 is available for 5G parts\n");
|
||||
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;
|
||||
}
|
||||
|
||||
|
12
ecp5/pack.cc
12
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
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
{
|
||||
disableActions();
|
||||
std::ifstream f(filename);
|
||||
if (parse_json_file(f, filename, ctx.get())) {
|
||||
log("Loading design successful.\n");
|
||||
Q_EMIT updateTreeView();
|
||||
updateLoaded();
|
||||
} else {
|
||||
actionLoadJSON->setEnabled(true);
|
||||
log("Loading design failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMainWindow::open_json()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
|
||||
if (!fileName.isEmpty()) {
|
||||
load_json(fileName.toStdString());
|
||||
disableActions();
|
||||
ctx = handler->load_json(fileName.toStdString());
|
||||
Q_EMIT contextChanged(ctx.get());
|
||||
Q_EMIT updateTreeView();
|
||||
log("Loading design successful.\n");
|
||||
updateActions();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMainWindow::save_json()
|
||||
{
|
||||
QString fileName = QFileDialog::getSaveFileName(this, QString("Save JSON"), QString(), QString("*.json"));
|
||||
if (!fileName.isEmpty()) {
|
||||
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()
|
||||
void BaseMainWindow::updateActions()
|
||||
{
|
||||
disableActions();
|
||||
if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
|
||||
actionPack->setEnabled(true);
|
||||
onJsonLoaded();
|
||||
onProjectLoaded();
|
||||
}
|
||||
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::projectLoad(std::string filename)
|
||||
{
|
||||
ProjectHandler proj;
|
||||
disableActions();
|
||||
ctx = proj.load(filename);
|
||||
Q_EMIT contextChanged(ctx.get());
|
||||
log_info("Loaded project %s...\n", filename.c_str());
|
||||
updateLoaded();
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -4,3 +4,4 @@ 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 '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
|
||||
convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'JSON'" resources/save.png resources/save_json.png
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
BIN
gui/resources/save_json.png
Normal file
BIN
gui/resources/save_json.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
@ -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"))
|
||||
|
@ -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?)
|
||||
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"
|
||||
std::string fbp_value =
|
||||
feedback_path == "DELAY"
|
||||
? "0"
|
||||
: feedback_path == "SIMPLE"
|
||||
? "1"
|
||||
: feedback_path == "PHASE_AND_DELAY"
|
||||
? "2"
|
||||
: feedback_path == "EXTERNAL" ? "6" : feedback_path;
|
||||
: 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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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,6 +668,7 @@ 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.
|
||||
//
|
||||
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()));
|
||||
@ -683,6 +727,13 @@ static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, const string
|
||||
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,
|
||||
const string &portname, JsonNode *node)
|
||||
{
|
||||
@ -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;
|
||||
|
@ -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
|
||||
|
180
json/jsonwrite.cc
Normal file
180
json/jsonwrite.cc
Normal file
@ -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> ¶meters,
|
||||
bool for_module = false)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto ¶m : 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
|
@ -17,21 +17,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "project.h"
|
||||
#include <boost/filesystem/convenience.hpp>
|
||||
#include <fstream>
|
||||
#include "log.h"
|
||||
#ifndef JSON_WRITER
|
||||
#define JSON_WRITER
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) {}
|
||||
|
||||
std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
|
||||
{
|
||||
ArchArgs chipArgs;
|
||||
return std::unique_ptr<Context>(new Context(chipArgs));
|
||||
}
|
||||
|
||||
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
|
Loading…
Reference in New Issue
Block a user