diff options
author | David Shah <dave@ds0.me> | 2019-07-03 12:39:38 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-03 12:39:38 +0100 |
commit | 8f2813279c5888e655ee6f50f198cf8cb11b0b50 (patch) | |
tree | 0aac9464d3ea87d37cd0689903ba08094b7c0984 | |
parent | ff958830d1097b9bfa3c3b34094e671741ef563d (diff) | |
parent | e27dc41a7646fd3377d2400059c916fbcc35119c (diff) | |
download | nextpnr-8f2813279c5888e655ee6f50f198cf8cb11b0b50.tar.gz nextpnr-8f2813279c5888e655ee6f50f198cf8cb11b0b50.tar.bz2 nextpnr-8f2813279c5888e655ee6f50f198cf8cb11b0b50.zip |
Merge pull request #284 from YosysHQ/json_write
Initial support for writing to json files from nextpnr.
47 files changed, 1042 insertions, 804 deletions
diff --git a/common/command.cc b/common/command.cc index 3eafdb17..8acbafd2 100644 --- a/common/command.cc +++ b/common/command.cc @@ -27,6 +27,7 @@ #include "pybindings.h" #endif +#include <boost/algorithm/string.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/filesystem/convenience.hpp> #include <boost/program_options.hpp> @@ -35,6 +36,7 @@ #include "command.h" #include "design_utils.h" #include "jsonparse.h" +#include "jsonwrite.h" #include "log.h" #include "timing.h" #include "util.h" @@ -120,6 +122,7 @@ po::options_description CommandHandler::getGeneralOptions() #endif general.add_options()("json", po::value<std::string>(), "JSON design file to ingest"); + general.add_options()("write", po::value<std::string>(), "JSON design file to write"); general.add_options()("seed", po::value<int>(), "seed value for random number generator"); general.add_options()("randomize-seed,r", "randomize seed value for random number generator"); @@ -135,6 +138,9 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("placer-budgets", "use budget rather than criticality in placer timing weights"); general.add_options()("pack-only", "pack design only without placement or routing"); + general.add_options()("no-route", "process design without routing"); + general.add_options()("no-place", "process design without placement"); + general.add_options()("no-pack", "process design without packing"); general.add_options()("ignore-loops", "ignore combinational loops in timing analysis"); @@ -143,13 +149,14 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("freq", po::value<double>(), "set target frequency for design in MHz"); general.add_options()("timing-allow-fail", "allow timing to fail in design"); general.add_options()("no-tmdriv", "disable timing-driven placement"); - general.add_options()("save", po::value<std::string>(), "project file to write"); - general.add_options()("load", po::value<std::string>(), "project file to read"); return general; } void CommandHandler::setupContext(Context *ctx) { + if (ctx->settings.find(ctx->id("seed")) != ctx->settings.end()) + ctx->rngstate = ctx->setting<uint64_t>("seed"); + if (vm.count("verbose")) { ctx->verbose = true; } @@ -177,9 +184,9 @@ void CommandHandler::setupContext(Context *ctx) } if (vm.count("slack_redist_iter")) { - ctx->slack_redist_iter = vm["slack_redist_iter"].as<int>(); + ctx->settings[ctx->id("slack_redist_iter")] = vm["slack_redist_iter"].as<std::string>(); if (vm.count("freq") && vm["freq"].as<double>() == 0) { - ctx->auto_freq = true; + ctx->settings[ctx->id("auto_freq")] = std::to_string(true); #ifndef NO_GUI if (!vm.count("gui")) #endif @@ -188,11 +195,11 @@ void CommandHandler::setupContext(Context *ctx) } if (vm.count("ignore-loops")) { - settings->set("timing/ignoreLoops", true); + ctx->settings[ctx->id("timing/ignoreLoops")] = std::to_string(true); } if (vm.count("timing-allow-fail")) { - settings->set("timing/allowFail", true); + ctx->settings[ctx->id("timing/allowFail")] = std::to_string(true); } if (vm.count("placer")) { @@ -201,30 +208,43 @@ void CommandHandler::setupContext(Context *ctx) Arch::availablePlacers.end()) log_error("Placer algorithm '%s' is not supported (available options: %s)\n", placer.c_str(), boost::algorithm::join(Arch::availablePlacers, ", ").c_str()); - settings->set("placer", placer); - } else { - settings->set("placer", Arch::defaultPlacer); + ctx->settings[ctx->id("placer")] = placer; } if (vm.count("cstrweight")) { - settings->set("placer1/constraintWeight", vm["cstrweight"].as<float>()); + ctx->settings[ctx->id("placer1/constraintWeight")] = std::to_string(vm["cstrweight"].as<float>()); } if (vm.count("starttemp")) { - settings->set("placer1/startTemp", vm["starttemp"].as<float>()); + ctx->settings[ctx->id("placer1/startTemp")] = std::to_string(vm["starttemp"].as<float>()); } if (vm.count("placer-budgets")) { - settings->set("placer1/budgetBased", true); + ctx->settings[ctx->id("placer1/budgetBased")] = std::to_string(true); } if (vm.count("freq")) { auto freq = vm["freq"].as<double>(); if (freq > 0) - ctx->target_freq = freq * 1e6; + ctx->settings[ctx->id("target_freq")] = std::to_string(freq * 1e6); } - ctx->timing_driven = true; if (vm.count("no-tmdriv")) - ctx->timing_driven = false; + ctx->settings[ctx->id("timing_driven")] = std::to_string(false); + + // Setting default values + if (ctx->settings.find(ctx->id("target_freq")) == ctx->settings.end()) + ctx->settings[ctx->id("target_freq")] = std::to_string(12e6); + if (ctx->settings.find(ctx->id("timing_driven")) == ctx->settings.end()) + ctx->settings[ctx->id("timing_driven")] = std::to_string(true); + if (ctx->settings.find(ctx->id("slack_redist_iter")) == ctx->settings.end()) + ctx->settings[ctx->id("slack_redist_iter")] = "0"; + if (ctx->settings.find(ctx->id("auto_freq")) == ctx->settings.end()) + ctx->settings[ctx->id("auto_freq")] = std::to_string(false); + if (ctx->settings.find(ctx->id("placer")) == ctx->settings.end()) + ctx->settings[ctx->id("placer")] = Arch::defaultPlacer; + + ctx->settings[ctx->id("arch.name")] = std::string(ctx->archId().c_str(ctx)); + ctx->settings[ctx->id("arch.type")] = std::string(ctx->archArgsToId(ctx->archArgs()).c_str(ctx)); + ctx->settings[ctx->id("seed")] = std::to_string(ctx->rngstate); } int CommandHandler::executeMain(std::unique_ptr<Context> ctx) @@ -237,19 +257,17 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx) #ifndef NO_GUI if (vm.count("gui")) { Application a(argc, argv, (vm.count("gui-no-aa") > 0)); - MainWindow w(std::move(ctx), chipArgs); + MainWindow w(std::move(ctx), this); try { if (vm.count("json")) { std::string filename = vm["json"].as<std::string>(); std::ifstream f(filename); - w.notifyChangeContext(); if (!parse_json_file(f, filename, w.getContext())) log_error("Loading design failed.\n"); customAfterLoad(w.getContext()); - w.updateLoaded(); - } else if (vm.count("load")) { - w.projectLoad(vm["load"].as<std::string>()); + w.notifyChangeContext(); + w.updateActions(); } else w.notifyChangeContext(); } catch (log_execution_error_exception) { @@ -280,31 +298,42 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx) execute_python_file(filename.c_str()); } else #endif - if (vm.count("json") || vm.count("load")) { - run_script_hook("pre-pack"); - if (!ctx->pack() && !ctx->force) - log_error("Packing design failed.\n"); + if (vm.count("json")) { + bool do_pack = vm.count("pack-only") != 0 || vm.count("no-pack") == 0; + bool do_place = vm.count("pack-only") == 0 && vm.count("no-place") == 0; + bool do_route = vm.count("pack-only") == 0 && vm.count("no-route") == 0; + + if (do_pack) { + run_script_hook("pre-pack"); + if (!ctx->pack() && !ctx->force) + log_error("Packing design failed.\n"); + } assign_budget(ctx.get()); ctx->check(); print_utilisation(ctx.get()); - run_script_hook("pre-place"); - if (!vm.count("pack-only")) { + if (do_place) { + run_script_hook("pre-place"); if (!ctx->place() && !ctx->force) log_error("Placing design failed.\n"); ctx->check(); - run_script_hook("pre-route"); + } + if (do_route) { + run_script_hook("pre-route"); if (!ctx->route() && !ctx->force) log_error("Routing design failed.\n"); + run_script_hook("post-route"); } - run_script_hook("post-route"); customBitstream(ctx.get()); } - if (vm.count("save")) { - project.save(ctx.get(), vm["save"].as<std::string>()); + if (vm.count("write")) { + std::string filename = vm["write"].as<std::string>(); + std::ofstream f(filename); + if (!write_json_file(f, filename, ctx.get())) + log_error("Saving design failed.\n"); } #ifndef NO_PYTHON @@ -341,13 +370,14 @@ int CommandHandler::exec() if (executeBeforeContext()) return 0; - std::unique_ptr<Context> ctx; - if (vm.count("load") && vm.count("gui") == 0) { - ctx = project.load(vm["load"].as<std::string>()); - } else { - ctx = createContext(); + std::unordered_map<std::string, Property> values; + if (vm.count("json")) { + std::string filename = vm["json"].as<std::string>(); + std::ifstream f(filename); + if (!load_json_settings(f, filename, values)) + log_error("Loading design failed.\n"); } - settings = std::unique_ptr<Settings>(new Settings(ctx.get())); + std::unique_ptr<Context> ctx = createContext(values); setupContext(ctx.get()); setupArchContext(ctx.get()); int rc = executeMain(std::move(ctx)); @@ -359,6 +389,27 @@ int CommandHandler::exec() } } +std::unique_ptr<Context> CommandHandler::load_json(std::string filename) +{ + vm.clear(); + std::unordered_map<std::string, Property> values; + { + std::ifstream f(filename); + if (!load_json_settings(f, filename, values)) + log_error("Loading design failed.\n"); + } + std::unique_ptr<Context> ctx = createContext(values); + setupContext(ctx.get()); + setupArchContext(ctx.get()); + { + std::ifstream f(filename); + if (!parse_json_file(f, filename, ctx.get())) + log_error("Loading design failed.\n"); + } + customAfterLoad(ctx.get()); + return ctx; +} + void CommandHandler::run_script_hook(const std::string &name) { #ifndef NO_PYTHON diff --git a/common/command.h b/common/command.h index d0f1d328..e52b5a48 100644 --- a/common/command.h +++ b/common/command.h @@ -22,9 +22,9 @@ #define COMMAND_H #include <boost/program_options.hpp> +#include <fstream> +#include "log.h" #include "nextpnr.h" -#include "project.h" -#include "settings.h" NEXTPNR_NAMESPACE_BEGIN @@ -37,10 +37,11 @@ class CommandHandler virtual ~CommandHandler(){}; int exec(); + std::unique_ptr<Context> load_json(std::string filename); protected: virtual void setupArchContext(Context *ctx) = 0; - virtual std::unique_ptr<Context> createContext() = 0; + virtual std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) = 0; virtual po::options_description getArchOptions() = 0; virtual void validate(){}; virtual void customAfterLoad(Context *ctx){}; @@ -58,15 +59,12 @@ class CommandHandler protected: po::variables_map vm; - ArchArgs chipArgs; - std::unique_ptr<Settings> settings; private: po::options_description options; po::positional_options_description pos; int argc; char **argv; - ProjectHandler project; std::ofstream logfile; }; diff --git a/common/nextpnr.cc b/common/nextpnr.cc index daaadf28..d4cc4917 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -18,6 +18,7 @@ */ #include "nextpnr.h" +#include <boost/algorithm/string.hpp> #include "log.h" NEXTPNR_NAMESPACE_BEGIN @@ -453,4 +454,118 @@ DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y) return dxy; } +void BaseCtx::archInfoToAttributes() +{ + for (auto &cell : cells) { + auto ci = cell.second.get(); + if (ci->bel != BelId()) { + if (ci->attrs.find(id("BEL")) != ci->attrs.end()) { + ci->attrs.erase(ci->attrs.find(id("BEL"))); + } + ci->attrs[id("NEXTPNR_BEL")] = getCtx()->getBelName(ci->bel).c_str(this); + ci->attrs[id("BEL_STRENGTH")] = std::to_string((int)ci->belStrength); + } + if (ci->constr_x != ci->UNCONSTR) + ci->attrs[id("CONSTR_X")] = std::to_string(ci->constr_x); + if (ci->constr_y != ci->UNCONSTR) + ci->attrs[id("CONSTR_Y")] = std::to_string(ci->constr_y); + if (ci->constr_z != ci->UNCONSTR) { + ci->attrs[id("CONSTR_Z")] = std::to_string(ci->constr_z); + ci->attrs[id("CONSTR_ABS_Z")] = std::to_string(ci->constr_abs_z ? 1 : 0); + } + if (ci->constr_parent != nullptr) + ci->attrs[id("CONSTR_PARENT")] = ci->constr_parent->name.c_str(this); + if (!ci->constr_children.empty()) { + std::string constr = ""; + for (auto &item : ci->constr_children) { + if (!constr.empty()) + constr += std::string(";"); + constr += item->name.c_str(this); + } + ci->attrs[id("CONSTR_CHILDREN")] = constr; + } + } + for (auto &net : getCtx()->nets) { + auto ni = net.second.get(); + std::string routing; + bool first = true; + for (auto &item : ni->wires) { + if (!first) + routing += ";"; + routing += getCtx()->getWireName(item.first).c_str(this); + routing += ";"; + if (item.second.pip != PipId()) + routing += getCtx()->getPipName(item.second.pip).c_str(this); + routing += ";" + std::to_string(item.second.strength); + first = false; + } + ni->attrs[id("ROUTING")] = routing; + } +} + +void BaseCtx::attributesToArchInfo() +{ + for (auto &cell : cells) { + auto ci = cell.second.get(); + auto val = ci->attrs.find(id("NEXTPNR_BEL")); + if (val != ci->attrs.end()) { + auto str = ci->attrs.find(id("BEL_STRENGTH")); + PlaceStrength strength = PlaceStrength::STRENGTH_USER; + if (str != ci->attrs.end()) + strength = (PlaceStrength)std::stoi(str->second.str); + + BelId b = getCtx()->getBelByName(id(val->second.str)); + getCtx()->bindBel(b, ci, strength); + } + val = ci->attrs.find(id("CONSTR_X")); + if (val != ci->attrs.end()) + ci->constr_x = std::stoi(val->second.str); + + val = ci->attrs.find(id("CONSTR_Y")); + if (val != ci->attrs.end()) + ci->constr_y = std::stoi(val->second.str); + + val = ci->attrs.find(id("CONSTR_Z")); + if (val != ci->attrs.end()) + ci->constr_z = std::stoi(val->second.str); + + val = ci->attrs.find(id("CONSTR_ABS_Z")); + if (val != ci->attrs.end()) + ci->constr_abs_z = std::stoi(val->second.str) == 1; + + val = ci->attrs.find(id("CONSTR_PARENT")); + if (val != ci->attrs.end()) { + auto parent = cells.find(id(val->second.str)); + if (parent != cells.end()) + ci->constr_parent = parent->second.get(); + } + val = ci->attrs.find(id("CONSTR_CHILDREN")); + if (val != ci->attrs.end()) { + std::vector<std::string> strs; + boost::split(strs, val->second.str, boost::is_any_of(";")); + for (auto val : strs) { + ci->constr_children.push_back(cells.find(id(val.c_str()))->second.get()); + } + } + } + for (auto &net : getCtx()->nets) { + auto ni = net.second.get(); + auto val = ni->attrs.find(id("ROUTING")); + if (val != ni->attrs.end()) { + std::vector<std::string> strs; + boost::split(strs, val->second.str, boost::is_any_of(";")); + for (size_t i = 0; i < strs.size() / 3; i++) { + std::string wire = strs[i * 3]; + std::string pip = strs[i * 3 + 1]; + PlaceStrength strength = (PlaceStrength)std::stoi(strs[i * 3 + 2]); + if (pip.empty()) + getCtx()->bindWire(getCtx()->getWireByName(id(wire)), ni, strength); + else + getCtx()->bindPip(getCtx()->getPipByName(id(pip)), ni, strength); + } + } + } + getCtx()->assignArchInfo(); +} + NEXTPNR_NAMESPACE_END diff --git a/common/nextpnr.h b/common/nextpnr.h index fc49300e..3f434580 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -32,6 +32,7 @@ #include <vector> #include <boost/functional/hash.hpp> +#include <boost/lexical_cast.hpp> #ifndef NEXTPNR_H #define NEXTPNR_H @@ -286,6 +287,44 @@ struct PipMap PlaceStrength strength = STRENGTH_NONE; }; +struct Property +{ + bool is_string; + + std::string str; + int num; + + std::string::iterator begin() { return str.begin(); } + std::string::iterator end() { return str.end(); } + + bool isString() const { return is_string; } + + void setNumber(int val) + { + is_string = false; + num = val; + str = std::to_string(val); + } + void setString(std::string val) + { + is_string = true; + str = val; + } + + const char *c_str() const { return str.c_str(); } + operator std::string() const { return str; } + + bool operator==(const std::string other) const { return str == other; } + bool operator!=(const std::string other) const { return str != other; } + + Property &operator=(std::string other) + { + is_string = true; + str = other; + return *this; + } +}; + struct ClockConstraint; struct NetInfo : ArchNetInfo @@ -295,7 +334,7 @@ struct NetInfo : ArchNetInfo PortRef driver; std::vector<PortRef> users; - std::unordered_map<IdString, std::string> attrs; + std::unordered_map<IdString, Property> attrs; // wire -> uphill_pip std::unordered_map<WireId, PipMap> wires; @@ -328,7 +367,7 @@ struct CellInfo : ArchCellInfo int32_t udata; std::unordered_map<IdString, PortInfo> ports; - std::unordered_map<IdString, std::string> attrs, params; + std::unordered_map<IdString, Property> attrs, params; BelId bel; PlaceStrength belStrength = STRENGTH_NONE; @@ -506,15 +545,21 @@ struct BaseCtx mutable std::vector<const std::string *> *idstring_idx_to_str; // Project settings and config switches - std::unordered_map<IdString, std::string> settings; + std::unordered_map<IdString, Property> settings; // Placed nets and cells. std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets; std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells; + // Top-level ports + std::unordered_map<IdString, PortInfo> ports; + // Floorplanning regions std::unordered_map<IdString, std::unique_ptr<Region>> region; + // Context meta data + std::unordered_map<IdString, Property> attrs; + BaseCtx() { idstring_str_to_idx = new std::unordered_map<std::string, int>; @@ -646,6 +691,9 @@ struct BaseCtx // Workaround for lack of wrappable constructors DecalXY constructDecalXY(DecalId decal, float x, float y); + + void archInfoToAttributes(); + void attributesToArchInfo(); }; NEXTPNR_NAMESPACE_END @@ -659,10 +707,6 @@ struct Context : Arch, DeterministicRNG bool verbose = false; bool debug = false; bool force = false; - bool timing_driven = true; - float target_freq = 12e6; - bool auto_freq = false; - int slack_redist_iter = 0; Context(ArchArgs args) : Arch(args) {} @@ -683,6 +727,26 @@ struct Context : Arch, DeterministicRNG void check() const; void archcheck() const; + + template <typename T> T setting(const char *name, T defaultValue) + { + IdString new_id = id(name); + if (settings.find(new_id) != settings.end()) + return boost::lexical_cast<T>(settings.find(new_id)->second.str); + else + settings[id(name)] = std::to_string(defaultValue); + + return defaultValue; + } + + template <typename T> T setting(const char *name) const + { + IdString new_id = id(name); + if (settings.find(new_id) != settings.end()) + return boost::lexical_cast<T>(settings.find(new_id)->second.str); + else + throw std::runtime_error("settings does not exists"); + } }; NEXTPNR_NAMESPACE_END diff --git a/common/place_common.cc b/common/place_common.cc index 73a320d0..cb9799b5 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -37,7 +37,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type if (driver_gb) return 0; int clock_count; - bool timing_driven = ctx->timing_driven && type == MetricType::COST && + bool timing_driven = ctx->setting<bool>("timing_driven") && type == MetricType::COST && ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE; delay_t negative_slack = 0; delay_t worst_slack = std::numeric_limits<delay_t>::max(); diff --git a/common/placer1.cc b/common/placer1.cc index a8ddd8a6..cb7ae847 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -219,7 +219,7 @@ class SAPlacer if ((placed_cells - constr_placed_cells) % 500 != 0) log_info(" initial placement placed %d/%d cells\n", int(placed_cells - constr_placed_cells), int(autoplaced.size())); - if (cfg.budgetBased && ctx->slack_redist_iter > 0) + if (cfg.budgetBased && cfg.slack_redist_iter > 0) assign_budget(ctx); ctx->yield(); auto iplace_end = std::chrono::high_resolution_clock::now(); @@ -370,16 +370,16 @@ class SAPlacer ctx->shuffle(autoplaced); // Legalisation is a big change so force a slack redistribution here - if (ctx->slack_redist_iter > 0 && cfg.budgetBased) + if (cfg.slack_redist_iter > 0 && cfg.budgetBased) assign_budget(ctx, true /* quiet */); } require_legal = false; - } else if (cfg.budgetBased && ctx->slack_redist_iter > 0 && iter % ctx->slack_redist_iter == 0) { + } else if (cfg.budgetBased && cfg.slack_redist_iter > 0 && iter % cfg.slack_redist_iter == 0) { assign_budget(ctx, true /* quiet */); } // Invoke timing analysis to obtain criticalities - if (!cfg.budgetBased && ctx->timing_driven) + if (!cfg.budgetBased && cfg.timing_driven) get_criticalities(ctx, &net_crit); // Need to rebuild costs after criticalities change setup_costs(); @@ -804,7 +804,7 @@ class SAPlacer if (ignore_net(ni)) continue; net_bounds[ni->udata] = get_net_bounds(ni); - if (ctx->timing_driven && int(ni->users.size()) < cfg.timingFanoutThresh) + if (cfg.timing_driven && int(ni->users.size()) < cfg.timingFanoutThresh) for (size_t i = 0; i < ni->users.size(); i++) net_arc_tcost[ni->udata][i] = get_timing_cost(ni, i); } @@ -1021,7 +1021,7 @@ class SAPlacer } } - if (ctx->timing_driven && int(pn->users.size()) < cfg.timingFanoutThresh) { + if (cfg.timing_driven && int(pn->users.size()) < cfg.timingFanoutThresh) { // Output ports - all arcs change timing if (port.second.type == PORT_OUT) { int cc; @@ -1061,7 +1061,7 @@ class SAPlacer if (md.already_bounds_changed_x[bc] == MoveChangeData::NO_CHANGE) md.wirelen_delta += md.new_net_bounds[bc].hpwl() - net_bounds[bc].hpwl(); - if (ctx->timing_driven) { + if (cfg.timing_driven) { for (const auto &tc : md.changed_arcs) { double old_cost = net_arc_tcost.at(tc.first).at(tc.second); double new_cost = get_timing_cost(net_by_udata.at(tc.first), tc.second); @@ -1131,13 +1131,15 @@ class SAPlacer Placer1Cfg cfg; }; -Placer1Cfg::Placer1Cfg(Context *ctx) : Settings(ctx) +Placer1Cfg::Placer1Cfg(Context *ctx) { - constraintWeight = get<float>("placer1/constraintWeight", 10); - minBelsForGridPick = get<int>("placer1/minBelsForGridPick", 64); - budgetBased = get<bool>("placer1/budgetBased", false); - startTemp = get<float>("placer1/startTemp", 1); + constraintWeight = ctx->setting<float>("placer1/constraintWeight", 10); + minBelsForGridPick = ctx->setting<int>("placer1/minBelsForGridPick", 64); + budgetBased = ctx->setting<bool>("placer1/budgetBased", false); + startTemp = ctx->setting<float>("placer1/startTemp", 1); timingFanoutThresh = std::numeric_limits<int>::max(); + timing_driven = ctx->setting<bool>("timing_driven"); + slack_redist_iter = ctx->setting<int>("slack_redist_iter"); } bool placer1(Context *ctx, Placer1Cfg cfg) diff --git a/common/placer1.h b/common/placer1.h index 4c7c7339..1356db3e 100644 --- a/common/placer1.h +++ b/common/placer1.h @@ -19,12 +19,12 @@ #ifndef PLACE_H #define PLACE_H +#include "log.h" #include "nextpnr.h" -#include "settings.h" NEXTPNR_NAMESPACE_BEGIN -struct Placer1Cfg : public Settings +struct Placer1Cfg { Placer1Cfg(Context *ctx); float constraintWeight; @@ -32,6 +32,8 @@ struct Placer1Cfg : public Settings bool budgetBased; float startTemp; int timingFanoutThresh; + bool timing_driven; + int slack_redist_iter; }; extern bool placer1(Context *ctx, Placer1Cfg cfg); diff --git a/common/placer_heap.cc b/common/placer_heap.cc index f9b639f8..cd800085 100644 --- a/common/placer_heap.cc +++ b/common/placer_heap.cc @@ -234,7 +234,7 @@ class HeAPPlacer std::chrono::duration<double>(run_stopt - run_startt).count()); } - if (ctx->timing_driven) + if (cfg.timing_driven) get_criticalities(ctx, &net_crit); if (legal_hpwl < best_hpwl) { @@ -1516,11 +1516,12 @@ int HeAPPlacer::CutSpreader::seq = 0; bool placer_heap(Context *ctx, PlacerHeapCfg cfg) { return HeAPPlacer(ctx, cfg).place(); } -PlacerHeapCfg::PlacerHeapCfg(Context *ctx) : Settings(ctx) +PlacerHeapCfg::PlacerHeapCfg(Context *ctx) { - alpha = get<float>("placerHeap/alpha", 0.1); - criticalityExponent = get<int>("placerHeap/criticalityExponent", 2); - timingWeight = get<int>("placerHeap/timingWeight", 10); + alpha = ctx->setting<float>("placerHeap/alpha", 0.1); + criticalityExponent = ctx->setting<int>("placerHeap/criticalityExponent", 2); + timingWeight = ctx->setting<int>("placerHeap/timingWeight", 10); + timing_driven = ctx->setting<bool>("timing_driven"); } NEXTPNR_NAMESPACE_END @@ -1538,7 +1539,7 @@ bool placer_heap(Context *ctx, PlacerHeapCfg cfg) return false; } -PlacerHeapCfg::PlacerHeapCfg(Context *ctx) : Settings(ctx) {} +PlacerHeapCfg::PlacerHeapCfg(Context *ctx) {} NEXTPNR_NAMESPACE_END diff --git a/common/placer_heap.h b/common/placer_heap.h index 841aa0d9..a018aef8 100644 --- a/common/placer_heap.h +++ b/common/placer_heap.h @@ -26,18 +26,19 @@ #ifndef PLACER_HEAP_H #define PLACER_HEAP_H +#include "log.h" #include "nextpnr.h" -#include "settings.h" NEXTPNR_NAMESPACE_BEGIN -struct PlacerHeapCfg : public Settings +struct PlacerHeapCfg { PlacerHeapCfg(Context *ctx); float alpha; float criticalityExponent; float timingWeight; + bool timing_driven; std::unordered_set<IdString> ioBufTypes; }; diff --git a/common/project.cc b/common/project.cc deleted file mode 100644 index b0ebe961..00000000 --- a/common/project.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "project.h" -#include <algorithm> -#include <boost/filesystem/convenience.hpp> -#include <boost/property_tree/json_parser.hpp> -#include <fstream> -#include "jsonparse.h" -#include "log.h" - -NEXTPNR_NAMESPACE_BEGIN - -boost::filesystem::path make_relative(boost::filesystem::path child, boost::filesystem::path parent) -{ - boost::filesystem::path::const_iterator parentIter = parent.begin(); - boost::filesystem::path::const_iterator childIter = child.begin(); - - while (parentIter != parent.end() && childIter != child.end() && (*childIter) == (*parentIter)) { - ++childIter; - ++parentIter; - } - - boost::filesystem::path finalPath; - while (parentIter != parent.end()) { - finalPath /= ".."; - ++parentIter; - } - - while (childIter != child.end()) { - finalPath /= *childIter; - ++childIter; - } - - return finalPath; -} - -void ProjectHandler::save(Context *ctx, std::string filename) -{ - try { - boost::filesystem::path proj(filename); - std::ofstream f(filename); - pt::ptree root; - - log_info("Saving project %s...\n", filename.c_str()); - log_break(); - - root.put("project.version", 1); - root.put("project.name", boost::filesystem::basename(filename)); - root.put("project.arch.name", ctx->archId().c_str(ctx)); - root.put("project.arch.type", ctx->archArgsToId(ctx->archArgs()).c_str(ctx)); - std::string fn = ctx->settings[ctx->id("input/json")]; - root.put("project.input.json", make_relative(fn, proj.parent_path()).string()); - root.put("project.params.freq", int(ctx->target_freq / 1e6)); - root.put("project.params.seed", ctx->rngstate); - saveArch(ctx, root, proj.parent_path().string()); - for (auto const &item : ctx->settings) { - std::string path = "project.settings."; - path += item.first.c_str(ctx); - std::replace(path.begin(), path.end(), '/', '.'); - root.put(path, item.second); - } - pt::write_json(f, root); - } catch (...) { - log_error("Error saving project file.\n"); - } -} - -void addSettings(Context *ctx, std::string path, pt::ptree sub) -{ - for (pt::ptree::value_type &v : sub) { - const std::string &key = v.first; - const boost::property_tree::ptree &subtree = v.second; - if (subtree.empty()) { - ctx->settings.emplace(ctx->id(path + key), subtree.get_value<std::string>().c_str()); - } else { - addSettings(ctx, path + key + "/", subtree); - } - } -} - -std::unique_ptr<Context> ProjectHandler::load(std::string filename) -{ - std::unique_ptr<Context> ctx; - try { - pt::ptree root; - boost::filesystem::path proj(filename); - pt::read_json(filename, root); - log_info("Loading project %s...\n", filename.c_str()); - log_break(); - - int version = root.get<int>("project.version"); - if (version != 1) - log_error("Wrong project format version.\n"); - - ctx = createContext(root); - - std::string arch_name = root.get<std::string>("project.arch.name"); - if (arch_name != ctx->archId().c_str(ctx.get())) - log_error("Unsuported project architecture.\n"); - - auto project = root.get_child("project"); - auto input = project.get_child("input"); - std::string fn = input.get<std::string>("json"); - boost::filesystem::path json = proj.parent_path() / fn; - std::ifstream f(json.string()); - if (!parse_json_file(f, fn, ctx.get())) - log_error("Loading design failed.\n"); - - if (project.count("params")) { - auto params = project.get_child("params"); - if (params.count("freq")) - ctx->target_freq = params.get<double>("freq") * 1e6; - if (params.count("seed")) - ctx->rngseed(params.get<uint64_t>("seed")); - } - if (project.count("settings")) { - addSettings(ctx.get(), "", project.get_child("settings")); - } - - loadArch(ctx.get(), root, proj.parent_path().string()); - } catch (...) { - log_error("Error loading project file.\n"); - } - return ctx; -} - -NEXTPNR_NAMESPACE_END diff --git a/common/project.h b/common/project.h deleted file mode 100644 index 03b4b7b5..00000000 --- a/common/project.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef PROJECT_H -#define PROJECT_H - -#include <boost/filesystem/convenience.hpp> -#include <boost/property_tree/ptree.hpp> -#include "nextpnr.h" - -NEXTPNR_NAMESPACE_BEGIN - -namespace pt = boost::property_tree; - -struct ProjectHandler -{ - void save(Context *ctx, std::string filename); - std::unique_ptr<Context> load(std::string filename); - // implemented per arch - void saveArch(Context *ctx, pt::ptree &root, std::string path); - std::unique_ptr<Context> createContext(pt::ptree &root); - void loadArch(Context *ctx, pt::ptree &root, std::string path); -}; - -boost::filesystem::path make_relative(boost::filesystem::path child, boost::filesystem::path parent); - -NEXTPNR_NAMESPACE_END - -#endif // PROJECT_H diff --git a/common/pybindings.cc b/common/pybindings.cc index 60f87e27..52dd9717 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -122,7 +122,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME) .value("PORT_INOUT", PORT_INOUT) .export_values(); - typedef std::unordered_map<IdString, std::string> AttrMap; + typedef std::unordered_map<IdString, Property> AttrMap; typedef std::unordered_map<IdString, PortInfo> PortMap; typedef std::unordered_map<IdString, IdString> PinMap; typedef std::unordered_map<IdString, std::unique_ptr<Region>> RegionMap; diff --git a/common/router1.cc b/common/router1.cc index 28a422c8..02c817c7 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -733,12 +733,12 @@ struct Router1 NEXTPNR_NAMESPACE_BEGIN -Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) +Router1Cfg::Router1Cfg(Context *ctx) { - maxIterCnt = get<int>("router1/maxIterCnt", 200); - cleanupReroute = get<bool>("router1/cleanupReroute", true); - fullCleanupReroute = get<bool>("router1/fullCleanupReroute", true); - useEstimate = get<bool>("router1/useEstimate", true); + maxIterCnt = ctx->setting<int>("router1/maxIterCnt", 200); + cleanupReroute = ctx->setting<bool>("router1/cleanupReroute", true); + fullCleanupReroute = ctx->setting<bool>("router1/fullCleanupReroute", true); + useEstimate = ctx->setting<bool>("router1/useEstimate", true); wireRipupPenalty = ctx->getRipupDelayPenalty(); netRipupPenalty = 10 * ctx->getRipupDelayPenalty(); diff --git a/common/router1.h b/common/router1.h index 80d7aa96..a37784f4 100644 --- a/common/router1.h +++ b/common/router1.h @@ -20,12 +20,11 @@ #ifndef ROUTER1_H #define ROUTER1_H +#include "log.h" #include "nextpnr.h" -#include "settings.h" - NEXTPNR_NAMESPACE_BEGIN -struct Router1Cfg : Settings +struct Router1Cfg { Router1Cfg(Context *ctx); diff --git a/common/settings.h b/common/settings.h deleted file mode 100644 index b57947c9..00000000 --- a/common/settings.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ -#ifndef SETTINGS_H -#define SETTINGS_H - -#include <boost/lexical_cast.hpp> -#include "log.h" -#include "nextpnr.h" - -NEXTPNR_NAMESPACE_BEGIN - -class Settings -{ - public: - explicit Settings(Context *ctx) : ctx(ctx) {} - - template <typename T> T get(const char *name, T defaultValue) - { - try { - IdString id = ctx->id(name); - auto pair = ctx->settings.emplace(id, std::to_string(defaultValue)); - if (!pair.second) { - return boost::lexical_cast<T>(pair.first->second); - } - - } catch (boost::bad_lexical_cast &) { - log_error("Problem reading setting %s, using default value\n", name); - } - return defaultValue; - } - - template <typename T> void set(const char *name, T value); - - private: - Context *ctx; -}; - -template <typename T> inline void Settings::set(const char *name, T value) -{ - IdString id = ctx->id(name); - auto pair = ctx->settings.emplace(id, std::to_string(value)); - if (!pair.second) { - ctx->settings[pair.first->first] = value; - } -} - -template <> inline void Settings::set<std::string>(const char *name, std::string value) -{ - IdString id = ctx->id(name); - auto pair = ctx->settings.emplace(id, value); - if (!pair.second) { - ctx->settings[pair.first->first] = value; - } -} - -NEXTPNR_NAMESPACE_END - -#endif // SETTINGS_H diff --git a/common/timing.cc b/common/timing.cc index e67ac231..599d6dbd 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -113,7 +113,7 @@ struct Timing Timing(Context *ctx, bool net_delays, bool update, CriticalPathMap *crit_path = nullptr, DelayFrequency *slack_histogram = nullptr, NetCriticalityMap *net_crit = nullptr) - : ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->target_freq), + : ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->setting<float>("target_freq")), crit_path(crit_path), slack_histogram(slack_histogram), net_crit(net_crit), async_clock(ctx->id("$async$")) { @@ -121,7 +121,7 @@ struct Timing delay_t walk_paths() { - const auto clk_period = ctx->getDelayFromNS(1.0e9 / ctx->target_freq).maxDelay(); + const auto clk_period = ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq")).maxDelay(); // First, compute the topographical order of nets to walk through the circuit, assuming it is a _acyclic_ graph // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops @@ -658,17 +658,18 @@ void assign_budget(Context *ctx, bool quiet) { if (!quiet) { log_break(); - log_info("Annotating ports with timing budgets for target frequency %.2f MHz\n", ctx->target_freq / 1e6); + log_info("Annotating ports with timing budgets for target frequency %.2f MHz\n", + ctx->setting<float>("target_freq") / 1e6); } - Timing timing(ctx, ctx->slack_redist_iter > 0 /* net_delays */, true /* update */); + Timing timing(ctx, ctx->setting<int>("slack_redist_iter") > 0 /* net_delays */, true /* update */); timing.assign_budget(); if (!quiet || ctx->verbose) { for (auto &net : ctx->nets) { for (auto &user : net.second->users) { // Post-update check - if (!ctx->auto_freq && user.budget < 0) + if (!ctx->setting<bool>("auto_freq") && user.budget < 0) log_info("port %s.%s, connected to net '%s', has negative " "timing budget of %fns\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx), @@ -684,13 +685,14 @@ void assign_budget(Context *ctx, bool quiet) // For slack redistribution, if user has not specified a frequency dynamically adjust the target frequency to be the // currently achieved maximum - if (ctx->auto_freq && ctx->slack_redist_iter > 0) { - delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->target_freq); - ctx->target_freq = 1.0e9 / ctx->getDelayNS(default_slack - timing.min_slack); + if (ctx->setting<bool>("auto_freq") && ctx->setting<int>("slack_redist_iter") > 0) { + delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->setting<float>("target_freq")); + ctx->settings[ctx->id("target_freq")] = + std::to_string(1.0e9 / ctx->getDelayNS(default_slack - timing.min_slack)); if (ctx->verbose) log_info("minimum slack for this assign = %.2f ns, target Fmax for next " "update = %.2f MHz\n", - ctx->getDelayNS(timing.min_slack), ctx->target_freq / 1e6); + ctx->getDelayNS(timing.min_slack), ctx->setting<float>("target_freq") / 1e6); } if (!quiet) @@ -897,7 +899,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p for (auto &clock : clock_reports) { const auto &clock_name = clock.first.str(ctx); const int width = max_width - clock_name.size(); - float target = ctx->target_freq / 1e6; + float target = ctx->setting<float>("target_freq") / 1e6; if (ctx->nets.at(clock.first)->clkconstr) target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay()); diff --git a/common/timing_opt.h b/common/timing_opt.h index ceb35c71..775d9596 100644 --- a/common/timing_opt.h +++ b/common/timing_opt.h @@ -17,14 +17,14 @@ * */ +#include "log.h" #include "nextpnr.h" -#include "settings.h" NEXTPNR_NAMESPACE_BEGIN -struct TimingOptCfg : public Settings +struct TimingOptCfg { - TimingOptCfg(Context *ctx) : Settings(ctx) {} + TimingOptCfg(Context *ctx) {} // The timing optimiser will *only* optimise cells of these types // Normally these would only be logic cells (or tiles if applicable), the algorithm makes little sense diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 91db8d81..5b3cc660 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -526,12 +526,15 @@ bool Arch::place() } permute_luts(); + getCtx()->settings[getCtx()->id("place")] = "1"; + archInfoToAttributes(); return true; } bool Arch::route() { route_ecp5_globals(getCtx()); + assignArchInfo(); assign_budget(getCtx(), true); bool result = router1(getCtx(), Router1Cfg(getCtx())); @@ -560,6 +563,8 @@ bool Arch::route() log_info(" base %d adder %d\n", speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_base_delay, speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_fanout_adder); #endif + getCtx()->settings[getCtx()->id("route")] = "1"; + archInfoToAttributes(); return result; } diff --git a/ecp5/globals.cc b/ecp5/globals.cc index fae2c683..026f3a85 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -382,7 +382,7 @@ class Ecp5GlobalRouter glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx)); glbnet->driver.cell = dcc.get(); glbnet->driver.port = id_CLKO; - glbnet->is_global = true; + glbnet->attrs[ctx->id("ECP5_IS_GLOBAL")] = "1"; dcc->ports[id_CLKO].net = glbnet.get(); std::vector<PortRef> keep_users; diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc index 4ac70fc9..ceb1d7ae 100644 --- a/ecp5/lpf.cc +++ b/ecp5/lpf.cc @@ -133,7 +133,7 @@ bool Arch::applyLPF(std::string filename, std::istream &in) } if (!isempty(linebuf)) log_error("unexpected end of LPF file\n"); - settings.emplace(id("input/lpf"), filename); + settings[id("input/lpf")] = filename; return true; } catch (log_execution_error_exception) { return false; diff --git a/ecp5/main.cc b/ecp5/main.cc index bb18aa58..75126cea 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -34,7 +34,7 @@ class ECP5CommandHandler : public CommandHandler public: ECP5CommandHandler(int argc, char **argv); virtual ~ECP5CommandHandler(){}; - std::unique_ptr<Context> createContext() override; + std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override; void setupArchContext(Context *ctx) override{}; void customAfterLoad(Context *ctx) override; void validate() override; @@ -98,9 +98,25 @@ void ECP5CommandHandler::customBitstream(Context *ctx) write_bitstream(ctx, basecfg, textcfg); } -std::unique_ptr<Context> ECP5CommandHandler::createContext() +static std::string speedString(ArchArgs::SpeedGrade speed) { - chipArgs.type = ArchArgs::LFE5U_45F; + switch (speed) { + case ArchArgs::SPEED_6: + return "6"; + case ArchArgs::SPEED_7: + return "7"; + case ArchArgs::SPEED_8: + return "8"; + case ArchArgs::SPEED_8_5G: + return "8"; + } + return ""; +} + +std::unique_ptr<Context> ECP5CommandHandler::createContext(std::unordered_map<std::string, Property> &values) +{ + ArchArgs chipArgs; + chipArgs.type = ArchArgs::NONE; if (vm.count("25k")) chipArgs.type = ArchArgs::LFE5U_25F; @@ -122,34 +138,96 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext() chipArgs.type = ArchArgs::LFE5UM5G_85F; if (vm.count("package")) chipArgs.package = vm["package"].as<std::string>(); - else + + if (vm.count("speed")) { + int speed = vm["speed"].as<int>(); + switch (speed) { + case 6: + chipArgs.speed = ArchArgs::SPEED_6; + break; + case 7: + chipArgs.speed = ArchArgs::SPEED_7; + break; + case 8: + chipArgs.speed = ArchArgs::SPEED_8; + break; + default: + log_error("Unsupported speed grade '%d'\n", speed); + } + } else { + if (chipArgs.type == ArchArgs::LFE5UM5G_25F || chipArgs.type == ArchArgs::LFE5UM5G_45F || + chipArgs.type == ArchArgs::LFE5UM5G_85F) { + chipArgs.speed = ArchArgs::SPEED_8; + } else + chipArgs.speed = ArchArgs::SPEED_6; + } + if (values.find("arch.name") != values.end()) { + std::string arch_name = values["arch.name"].str; + if (arch_name != "ecp5") + log_error("Unsuported architecture '%s'.\n", arch_name.c_str()); + } + if (values.find("arch.type") != values.end()) { + std::string arch_type = values["arch.type"].str; + if (chipArgs.type != ArchArgs::NONE) + log_error("Overriding architecture is unsuported.\n"); + + if (arch_type == "lfe5u_25f") + chipArgs.type = ArchArgs::LFE5U_25F; + if (arch_type == "lfe5u_45f") + chipArgs.type = ArchArgs::LFE5U_45F; + if (arch_type == "lfe5u_85f") + chipArgs.type = ArchArgs::LFE5U_85F; + if (arch_type == "lfe5um_25f") + chipArgs.type = ArchArgs::LFE5UM_25F; + if (arch_type == "lfe5um_45f") + chipArgs.type = ArchArgs::LFE5UM_45F; + if (arch_type == "lfe5um_85f") + chipArgs.type = ArchArgs::LFE5UM_85F; + if (arch_type == "lfe5um5g_25f") + chipArgs.type = ArchArgs::LFE5UM5G_25F; + if (arch_type == "lfe5um5g_45f") + chipArgs.type = ArchArgs::LFE5UM5G_45F; + if (arch_type == "lfe5um5g_85f") + chipArgs.type = ArchArgs::LFE5UM5G_85F; + + if (chipArgs.type == ArchArgs::NONE) + log_error("Unsuported FPGA type '%s'.\n", arch_type.c_str()); + } + if (values.find("arch.package") != values.end()) { + if (vm.count("package")) + log_error("Overriding architecture is unsuported.\n"); + chipArgs.package = values["arch.package"].str; + } + if (values.find("arch.speed") != values.end()) { + std::string arch_speed = values["arch.speed"].str; + if (arch_speed == "6") + chipArgs.speed = ArchArgs::SPEED_6; + else if (arch_speed == "7") + chipArgs.speed = ArchArgs::SPEED_7; + else if (arch_speed == "8") + chipArgs.speed = ArchArgs::SPEED_8; + else + log_error("Unsuported speed '%s'.\n", arch_speed.c_str()); + } + if (chipArgs.type == ArchArgs::NONE) + chipArgs.type = ArchArgs::LFE5U_45F; + + if (chipArgs.package.empty()) chipArgs.package = "CABGA381"; + if (chipArgs.type == ArchArgs::LFE5UM5G_25F || chipArgs.type == ArchArgs::LFE5UM5G_45F || chipArgs.type == ArchArgs::LFE5UM5G_85F) { - if (vm.count("speed") && vm["speed"].as<int>() != 8) + if (chipArgs.speed != ArchArgs::SPEED_8) log_error("Only speed grade 8 is available for 5G parts\n"); - chipArgs.speed = ArchArgs::SPEED_8_5G; - } else { - if (vm.count("speed")) { - int speed = vm["speed"].as<int>(); - switch (speed) { - case 6: - chipArgs.speed = ArchArgs::SPEED_6; - break; - case 7: - chipArgs.speed = ArchArgs::SPEED_7; - break; - case 8: - chipArgs.speed = ArchArgs::SPEED_8; - break; - default: - log_error("Unsupported speed grade '%d'\n", speed); - } - } else { - chipArgs.speed = ArchArgs::SPEED_6; - } + else + chipArgs.speed = ArchArgs::SPEED_8_5G; } + auto ctx = std::unique_ptr<Context>(new Context(chipArgs)); + for (auto &val : values) + ctx->settings[ctx->id(val.first)] = val.second; + ctx->settings[ctx->id("arch.package")] = ctx->archArgs().package; + ctx->settings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed); return ctx; } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 7f00de1f..9e2bc326 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1508,7 +1508,8 @@ class Ecp5Packer std::unique_ptr<NetInfo> promoted_ecknet(new NetInfo); promoted_ecknet->name = eckname; - promoted_ecknet->is_global = true; // Prevents router etc touching this special net + promoted_ecknet->attrs[ctx->id("ECP5_IS_GLOBAL")] = + "1"; // Prevents router etc touching this special net eclk.buf = promoted_ecknet.get(); NPNR_ASSERT(!ctx->nets.count(eckname)); ctx->nets[eckname] = std::move(promoted_ecknet); @@ -1654,7 +1655,7 @@ class Ecp5Packer port.c_str(ctx), ci->name.c_str(ctx), usr.port.c_str(ctx), usr.cell->name.c_str(ctx)); } - pn->is_global = true; + pn->attrs[ctx->id("ECP5_IS_GLOBAL")] = "1"; } for (auto zport : @@ -1896,7 +1897,7 @@ class Ecp5Packer iol->params[ctx->id("DELAY.DEL_VALUE")] = std::to_string(lookup_delay(str_or_default(ci->params, ctx->id("DEL_MODE"), "USER_DEFINED"))); if (ci->params.count(ctx->id("DEL_VALUE")) && - ci->params.at(ctx->id("DEL_VALUE")).substr(0, 5) != "DELAY") + std::string(ci->params.at(ctx->id("DEL_VALUE"))).substr(0, 5) != "DELAY") iol->params[ctx->id("DELAY.DEL_VALUE")] = ci->params.at(ctx->id("DEL_VALUE")); if (ci->ports.count(id_LOADN)) replace_port(ci, id_LOADN, iol, id_LOADN); @@ -2428,6 +2429,8 @@ bool Arch::pack() Ecp5Packer(ctx).pack(); log_info("Checksum: 0x%08x\n", ctx->checksum()); assignArchInfo(); + ctx->settings[ctx->id("pack")] = "1"; + archInfoToAttributes(); return true; } catch (log_execution_error_exception) { assignArchInfo(); @@ -2469,6 +2472,9 @@ void Arch::assignArchInfo() ci->sliceInfo.has_l6mux = true; } } + for (auto net : sorted(nets)) { + net.second->is_global = bool_or_default(net.second->attrs, id("ECP5_IS_GLOBAL")); + } } NEXTPNR_NAMESPACE_END diff --git a/ecp5/project.cc b/ecp5/project.cc deleted file mode 100644 index 43318b1c..00000000 --- a/ecp5/project.cc +++ /dev/null @@ -1,55 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "project.h" -#include <boost/filesystem/convenience.hpp> -#include <boost/property_tree/json_parser.hpp> -#include <fstream> -#include "log.h" - -NEXTPNR_NAMESPACE_BEGIN - -void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) -{ - root.put("project.arch.package", ctx->archArgs().package); - root.put("project.arch.speed", ctx->archArgs().speed); -} - -std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root) -{ - ArchArgs chipArgs; - std::string arch_type = root.get<std::string>("project.arch.type"); - if (arch_type == "25k") { - chipArgs.type = ArchArgs::LFE5U_25F; - } - if (arch_type == "45k") { - chipArgs.type = ArchArgs::LFE5U_45F; - } - if (arch_type == "85k") { - chipArgs.type = ArchArgs::LFE5U_85F; - } - chipArgs.package = root.get<std::string>("project.arch.package"); - chipArgs.speed = ArchArgs::SpeedGrade(root.get<int>("project.arch.speed")); - - return std::unique_ptr<Context>(new Context(chipArgs)); -} - -void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {} - -NEXTPNR_NAMESPACE_END diff --git a/generic/arch.cc b/generic/arch.cc index 5617fa63..fa3c825d 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -496,13 +496,22 @@ bool Arch::place() std::string placer = str_or_default(settings, id("placer"), defaultPlacer); // FIXME: No HeAP because it needs a list of IO buffers if (placer == "sa") { - return placer1(getCtx(), Placer1Cfg(getCtx())); + bool retVal = placer1(getCtx(), Placer1Cfg(getCtx())); + getCtx()->settings[getCtx()->id("place")] = "1"; + archInfoToAttributes(); + return retVal; } else { log_error("Generic architecture does not support placer '%s'\n", placer.c_str()); } } -bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } +bool Arch::route() +{ + bool retVal = router1(getCtx(), Router1Cfg(getCtx())); + getCtx()->settings[getCtx()->id("route")] = "1"; + archInfoToAttributes(); + return retVal; +} // --------------------------------------------------------------- diff --git a/generic/main.cc b/generic/main.cc index c203f35c..ce1a51c7 100644 --- a/generic/main.cc +++ b/generic/main.cc @@ -32,7 +32,7 @@ class GenericCommandHandler : public CommandHandler public: GenericCommandHandler(int argc, char **argv); virtual ~GenericCommandHandler(){}; - std::unique_ptr<Context> createContext() override; + std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override; void setupArchContext(Context *ctx) override{}; void customBitstream(Context *ctx) override; @@ -51,8 +51,14 @@ po::options_description GenericCommandHandler::getArchOptions() void GenericCommandHandler::customBitstream(Context *ctx) {} -std::unique_ptr<Context> GenericCommandHandler::createContext() +std::unique_ptr<Context> GenericCommandHandler::createContext(std::unordered_map<std::string, Property> &values) { + ArchArgs chipArgs; + if (values.find("arch.name") != values.end()) { + std::string arch_name = values["arch.name"].str; + if (arch_name != "generic") + log_error("Unsuported architecture '%s'.\n", arch_name.c_str()); + } return std::unique_ptr<Context>(new Context(chipArgs)); } diff --git a/generic/pack.cc b/generic/pack.cc index 26ae9182..558eca7c 100644 --- a/generic/pack.cc +++ b/generic/pack.cc @@ -282,6 +282,7 @@ bool Arch::pack() pack_io(ctx); pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); + ctx->settings[ctx->id("pack")] = "1"; ctx->assignArchInfo(); log_info("Checksum: 0x%08x\n", ctx->checksum()); return true; diff --git a/gui/base.qrc b/gui/base.qrc index 644b16a6..85d1432a 100644 --- a/gui/base.qrc +++ b/gui/base.qrc @@ -22,6 +22,7 @@ <file>resources/route.png</file> <file>resources/time_add.png</file> <file>resources/open_json.png</file> + <file>resources/save_json.png</file> <file>resources/py.png</file> </qresource> </RCC> diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 346efb88..550a4b93 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -29,17 +29,17 @@ #include "designwidget.h"
#include "fpgaviewwidget.h"
#include "jsonparse.h"
+#include "jsonwrite.h"
#include "log.h"
#include "mainwindow.h"
-#include "project.h"
#include "pythontab.h"
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
NEXTPNR_NAMESPACE_BEGIN
-BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : QMainWindow(parent), chipArgs(args), ctx(std::move(context)), timing_driven(false)
+BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : QMainWindow(parent), handler(handler), ctx(std::move(context)), timing_driven(false)
{
initBasenameResource();
qRegisterMetaType<std::string>();
@@ -130,25 +130,6 @@ void BaseMainWindow::writeInfo(std::string text) { console->info(text); } void BaseMainWindow::createMenusAndBars()
{
// File menu / project toolbar actions
- actionNew = new QAction("New", this);
- actionNew->setIcon(QIcon(":/icons/resources/new.png"));
- actionNew->setShortcuts(QKeySequence::New);
- actionNew->setStatusTip("New project file");
- connect(actionNew, &QAction::triggered, this, &BaseMainWindow::new_proj);
-
- actionOpen = new QAction("Open", this);
- actionOpen->setIcon(QIcon(":/icons/resources/open.png"));
- actionOpen->setShortcuts(QKeySequence::Open);
- actionOpen->setStatusTip("Open an existing project file");
- connect(actionOpen, &QAction::triggered, this, &BaseMainWindow::open_proj);
-
- actionSave = new QAction("Save", this);
- actionSave->setIcon(QIcon(":/icons/resources/save.png"));
- actionSave->setShortcuts(QKeySequence::Save);
- actionSave->setStatusTip("Save existing project to disk");
- actionSave->setEnabled(false);
- connect(actionSave, &QAction::triggered, this, &BaseMainWindow::save_proj);
-
QAction *actionExit = new QAction("Exit", this);
actionExit->setIcon(QIcon(":/icons/resources/exit.png"));
actionExit->setShortcuts(QKeySequence::Quit);
@@ -158,13 +139,26 @@ void BaseMainWindow::createMenusAndBars() // Help menu actions
QAction *actionAbout = new QAction("About", this);
- // Design menu options
+ // Gile menu options
+ actionNew = new QAction("New", this);
+ actionNew->setIcon(QIcon(":/icons/resources/new.png"));
+ actionNew->setShortcuts(QKeySequence::New);
+ actionNew->setStatusTip("New project");
+ connect(actionNew, &QAction::triggered, this, &BaseMainWindow::new_proj);
+
actionLoadJSON = new QAction("Open JSON", this);
actionLoadJSON->setIcon(QIcon(":/icons/resources/open_json.png"));
actionLoadJSON->setStatusTip("Open an existing JSON file");
actionLoadJSON->setEnabled(true);
connect(actionLoadJSON, &QAction::triggered, this, &BaseMainWindow::open_json);
+ actionSaveJSON = new QAction("Save JSON", this);
+ actionSaveJSON->setIcon(QIcon(":/icons/resources/save_json.png"));
+ actionSaveJSON->setStatusTip("Write to JSON file");
+ actionSaveJSON->setEnabled(true);
+ connect(actionSaveJSON, &QAction::triggered, this, &BaseMainWindow::save_json);
+
+ // Design menu options
actionPack = new QAction("Pack", this);
actionPack->setIcon(QIcon(":/icons/resources/pack.png"));
actionPack->setStatusTip("Pack current design");
@@ -244,13 +238,12 @@ void BaseMainWindow::createMenusAndBars() // Add File menu actions
menuFile->addAction(actionNew);
- menuFile->addAction(actionOpen);
- menuFile->addAction(actionSave);
+ menuFile->addAction(actionLoadJSON);
+ menuFile->addAction(actionSaveJSON);
menuFile->addSeparator();
menuFile->addAction(actionExit);
// Add Design menu actions
- menuDesign->addAction(actionLoadJSON);
menuDesign->addAction(actionPack);
menuDesign->addAction(actionAssignBudget);
menuDesign->addAction(actionPlace);
@@ -261,17 +254,13 @@ void BaseMainWindow::createMenusAndBars() // Add Help menu actions
menuHelp->addAction(actionAbout);
- // Project toolbar
- QToolBar *projectToolBar = new QToolBar("Project");
- addToolBar(Qt::TopToolBarArea, projectToolBar);
- projectToolBar->addAction(actionNew);
- projectToolBar->addAction(actionOpen);
- projectToolBar->addAction(actionSave);
-
// Main action bar
mainActionBar = new QToolBar("Main");
addToolBar(Qt::TopToolBarArea, mainActionBar);
+ mainActionBar->addAction(actionNew);
mainActionBar->addAction(actionLoadJSON);
+ mainActionBar->addAction(actionSaveJSON);
+ mainActionBar->addSeparator();
mainActionBar->addAction(actionPack);
mainActionBar->addAction(actionAssignBudget);
mainActionBar->addAction(actionPlace);
@@ -304,25 +293,29 @@ void BaseMainWindow::createMenusAndBars() setStatusBar(statusBar);
}
-void BaseMainWindow::load_json(std::string filename)
+void BaseMainWindow::open_json()
{
- disableActions();
- std::ifstream f(filename);
- if (parse_json_file(f, filename, ctx.get())) {
- log("Loading design successful.\n");
+ QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
+ if (!fileName.isEmpty()) {
+ disableActions();
+ ctx = handler->load_json(fileName.toStdString());
+ Q_EMIT contextChanged(ctx.get());
Q_EMIT updateTreeView();
- updateLoaded();
- } else {
- actionLoadJSON->setEnabled(true);
- log("Loading design failed.\n");
+ log("Loading design successful.\n");
+ updateActions();
}
}
-void BaseMainWindow::open_json()
+void BaseMainWindow::save_json()
{
- QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
+ QString fileName = QFileDialog::getSaveFileName(this, QString("Save JSON"), QString(), QString("*.json"));
if (!fileName.isEmpty()) {
- load_json(fileName.toStdString());
+ std::string fn = fileName.toStdString();
+ std::ofstream f(fn);
+ if (write_json_file(f, fn, ctx.get()))
+ log("Saving JSON successful.\n");
+ else
+ log("Saving JSON failed.\n");
}
}
@@ -332,9 +325,7 @@ void BaseMainWindow::pack_finished(bool status) if (status) {
log("Packing design successful.\n");
Q_EMIT updateTreeView();
- actionPlace->setEnabled(true);
- actionAssignBudget->setEnabled(true);
- onPackFinished();
+ updateActions();
} else {
log("Packing design failed.\n");
}
@@ -345,8 +336,7 @@ void BaseMainWindow::budget_finish(bool status) disableActions();
if (status) {
log("Assigning timing budget successful.\n");
- actionPlace->setEnabled(true);
- onBudgetFinished();
+ updateActions();
} else {
log("Assigning timing budget failed.\n");
}
@@ -358,8 +348,7 @@ void BaseMainWindow::place_finished(bool status) if (status) {
log("Placing design successful.\n");
Q_EMIT updateTreeView();
- actionRoute->setEnabled(true);
- onPlaceFinished();
+ updateActions();
} else {
log("Placing design failed.\n");
}
@@ -370,7 +359,7 @@ void BaseMainWindow::route_finished(bool status) if (status) {
log("Routing design successful.\n");
Q_EMIT updateTreeView();
- onRouteFinished();
+ updateActions();
} else
log("Routing design failed.\n");
}
@@ -386,9 +375,6 @@ void BaseMainWindow::taskStarted() disableActions();
actionPause->setEnabled(true);
actionStop->setEnabled(true);
-
- actionNew->setEnabled(false);
- actionOpen->setEnabled(false);
}
void BaseMainWindow::taskPaused()
@@ -396,9 +382,6 @@ void BaseMainWindow::taskPaused() disableActions();
actionPlay->setEnabled(true);
actionStop->setEnabled(true);
-
- actionNew->setEnabled(false);
- actionOpen->setEnabled(false);
}
void BaseMainWindow::budget()
@@ -416,52 +399,32 @@ void BaseMainWindow::place() { Q_EMIT task->place(timing_driven); } void BaseMainWindow::disableActions()
{
- actionLoadJSON->setEnabled(false);
+ actionLoadJSON->setEnabled(true);
actionPack->setEnabled(false);
actionAssignBudget->setEnabled(false);
actionPlace->setEnabled(false);
actionRoute->setEnabled(false);
+
actionExecutePy->setEnabled(true);
actionPlay->setEnabled(false);
actionPause->setEnabled(false);
actionStop->setEnabled(false);
- actionNew->setEnabled(true);
- actionOpen->setEnabled(true);
-
- if (ctx->settings.find(ctx->id("input/json")) != ctx->settings.end())
- actionSave->setEnabled(true);
- else
- actionSave->setEnabled(false);
-
onDisableActions();
}
-void BaseMainWindow::updateLoaded()
-{
- disableActions();
- actionPack->setEnabled(true);
- onJsonLoaded();
- onProjectLoaded();
-}
-
-void BaseMainWindow::projectLoad(std::string filename)
+void BaseMainWindow::updateActions()
{
- ProjectHandler proj;
- disableActions();
- ctx = proj.load(filename);
- Q_EMIT contextChanged(ctx.get());
- log_info("Loaded project %s...\n", filename.c_str());
- updateLoaded();
-}
+ if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
+ actionPack->setEnabled(true);
+ else if (ctx->settings.find(ctx->id("place")) == ctx->settings.end()) {
+ actionAssignBudget->setEnabled(true);
+ actionPlace->setEnabled(true);
+ } else if (ctx->settings.find(ctx->id("route")) == ctx->settings.end())
+ actionRoute->setEnabled(true);
-void BaseMainWindow::open_proj()
-{
- QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj"));
- if (!fileName.isEmpty()) {
- projectLoad(fileName.toStdString());
- }
+ onUpdateActions();
}
void BaseMainWindow::execute_python()
@@ -473,18 +436,5 @@ void BaseMainWindow::execute_python() }
void BaseMainWindow::notifyChangeContext() { Q_EMIT contextChanged(ctx.get()); }
-void BaseMainWindow::save_proj()
-{
- if (currentProj.empty()) {
- QString fileName = QFileDialog::getSaveFileName(this, QString("Save Project"), QString(), QString("*.proj"));
- if (fileName.isEmpty())
- return;
- currentProj = fileName.toStdString();
- }
- if (!currentProj.empty()) {
- ProjectHandler proj;
- proj.save(ctx.get(), currentProj);
- }
-}
NEXTPNR_NAMESPACE_END
diff --git a/gui/basewindow.h b/gui/basewindow.h index 0b2d3fbc..305cb6c9 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -20,6 +20,7 @@ #ifndef BASEMAINWINDOW_H
#define BASEMAINWINDOW_H
+#include "command.h"
#include "nextpnr.h"
#include "worker.h"
@@ -45,25 +46,19 @@ class BaseMainWindow : public QMainWindow Q_OBJECT
public:
- explicit BaseMainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit BaseMainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~BaseMainWindow();
Context *getContext() { return ctx.get(); }
- void updateLoaded();
- void projectLoad(std::string filename);
+ void updateActions();
+
void notifyChangeContext();
protected:
void createMenusAndBars();
void disableActions();
- void load_json(std::string filename);
virtual void onDisableActions(){};
- virtual void onJsonLoaded(){};
- virtual void onProjectLoaded(){};
- virtual void onPackFinished(){};
- virtual void onBudgetFinished(){};
- virtual void onPlaceFinished(){};
- virtual void onRouteFinished(){};
+ virtual void onUpdateActions(){};
protected Q_SLOTS:
void writeInfo(std::string text);
@@ -71,10 +66,8 @@ class BaseMainWindow : public QMainWindow virtual void new_proj() = 0;
- void open_proj();
- void save_proj();
-
void open_json();
+ void save_json();
void budget();
void place();
@@ -95,7 +88,7 @@ class BaseMainWindow : public QMainWindow protected:
// state variables
- ArchArgs chipArgs;
+ CommandHandler *handler;
std::unique_ptr<Context> ctx;
TaskManager *task;
bool timing_driven;
@@ -116,10 +109,9 @@ class BaseMainWindow : public QMainWindow QProgressBar *progressBar;
QAction *actionNew;
- QAction *actionOpen;
- QAction *actionSave;
-
QAction *actionLoadJSON;
+ QAction *actionSaveJSON;
+
QAction *actionPack;
QAction *actionAssignBudget;
QAction *actionPlace;
diff --git a/gui/create_img.sh b/gui/create_img.sh index 1508d023..2e7b1f87 100755 --- a/gui/create_img.sh +++ b/gui/create_img.sh @@ -2,5 +2,6 @@ convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 ' convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'PCF'" resources/open.png ice40/resources/open_pcf.png convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'BASE'" resources/open.png ecp5/resources/open_base.png convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'LPF'" resources/open.png ecp5/resources/open_lpf.png -convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'ASC'" resources/save.png ice40/resources/save_asc.png -convert -font helvetica -fill red -pointsize 7 -gravity center -draw "text 2,8 'CONFIG'" resources/save.png ecp5/resources/save_config.png
\ No newline at end of file +convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'ASC'" resources/save.png ice40/resources/save_asc.png +convert -font helvetica -fill red -pointsize 7 -gravity center -draw "text 2,8 'CONFIG'" resources/save.png ecp5/resources/save_config.png +convert -font helvetica -fill red -pointsize 8 -gravity center -draw "text 2,8 'JSON'" resources/save.png resources/save_json.png diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc index c6c7bc97..880af047 100644 --- a/gui/ecp5/mainwindow.cc +++ b/gui/ecp5/mainwindow.cc @@ -30,8 +30,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : BaseMainWindow(std::move(context), args, parent)
+MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : BaseMainWindow(std::move(context), handler, parent)
{
initMainResource();
@@ -47,7 +47,7 @@ MainWindow::~MainWindow() {} void MainWindow::newContext(Context *ctx)
{
- std::string title = "nextpnr-generic - " + ctx->getChipName() + " ( " + chipArgs.package + " )";
+ std::string title = "nextpnr-ecp5 - " + ctx->getChipName() + " ( " + ctx->archArgs().package + " )";
setWindowTitle(title.c_str());
}
@@ -60,12 +60,6 @@ void MainWindow::createMenu() actionLoadLPF->setEnabled(false);
connect(actionLoadLPF, &QAction::triggered, this, &MainWindow::open_lpf);
- actionLoadBase = new QAction("Open Base Config", this);
- actionLoadBase->setIcon(QIcon(":/icons/resources/open_base.png"));
- actionLoadBase->setStatusTip("Open Base Config file");
- actionLoadBase->setEnabled(false);
- connect(actionLoadBase, &QAction::triggered, this, &MainWindow::open_base);
-
actionSaveConfig = new QAction("Save Bitstream", this);
actionSaveConfig->setIcon(QIcon(":/icons/resources/save_config.png"));
actionSaveConfig->setStatusTip("Save Bitstream config file");
@@ -75,12 +69,10 @@ void MainWindow::createMenu() // Add actions in menus
mainActionBar->addSeparator();
mainActionBar->addAction(actionLoadLPF);
- mainActionBar->addAction(actionLoadBase);
mainActionBar->addAction(actionSaveConfig);
menuDesign->addSeparator();
menuDesign->addAction(actionLoadLPF);
- menuDesign->addAction(actionLoadBase);
menuDesign->addAction(actionSaveConfig);
}
@@ -121,7 +113,7 @@ void MainWindow::new_proj() bool ok;
QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
if (ok && !item.isEmpty()) {
-
+ ArchArgs chipArgs;
chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(item);
QString package = QInputDialog::getItem(this, "Select package", "Package:", getSupportedPackages(chipArgs.type),
@@ -139,13 +131,6 @@ void MainWindow::new_proj() }
}
-void MainWindow::load_base_config(std::string filename)
-{
- disableActions();
- currentBaseConfig = filename;
- actionSaveConfig->setEnabled(true);
-}
-
void MainWindow::open_lpf()
{
QString fileName = QFileDialog::getOpenFileName(this, QString("Open LPF"), QString(), QString("*.lpf"));
@@ -162,21 +147,13 @@ void MainWindow::open_lpf() }
}
-void MainWindow::open_base()
-{
- QString fileName = QFileDialog::getOpenFileName(this, QString("Open Base Config"), QString(), QString("*.config"));
- if (!fileName.isEmpty()) {
- load_base_config(fileName.toStdString());
- }
-}
-
void MainWindow::save_config()
{
QString fileName = QFileDialog::getSaveFileName(this, QString("Save Bitstream"), QString(), QString("*.config"));
if (!fileName.isEmpty()) {
std::string fn = fileName.toStdString();
disableActions();
- write_bitstream(ctx.get(), currentBaseConfig, fileName.toStdString());
+ write_bitstream(ctx.get(), "", fileName.toStdString());
log("Saving Bitstream successful.\n");
}
}
@@ -184,18 +161,15 @@ void MainWindow::save_config() void MainWindow::onDisableActions()
{
actionLoadLPF->setEnabled(false);
- actionLoadBase->setEnabled(false);
actionSaveConfig->setEnabled(false);
}
-void MainWindow::onJsonLoaded() { actionLoadLPF->setEnabled(true); }
-
-void MainWindow::onRouteFinished() { actionLoadBase->setEnabled(true); }
-
-void MainWindow::onProjectLoaded()
+void MainWindow::onUpdateActions()
{
- if (ctx->settings.find(ctx->id("input/lpf")) != ctx->settings.end())
- actionLoadLPF->setEnabled(false);
+ if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
+ actionLoadLPF->setEnabled(true);
+ if (ctx->settings.find(ctx->id("route")) != ctx->settings.end())
+ actionSaveConfig->setEnabled(true);
}
NEXTPNR_NAMESPACE_END
diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h index 721c6c0b..e60fc5b0 100644 --- a/gui/ecp5/mainwindow.h +++ b/gui/ecp5/mainwindow.h @@ -29,34 +29,25 @@ class MainWindow : public BaseMainWindow Q_OBJECT
public:
- explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
void createMenu();
- void load_base_config(std::string filename);
protected:
void onDisableActions() override;
- void onJsonLoaded() override;
- void onRouteFinished() override;
- void onProjectLoaded() override;
+ void onUpdateActions() override;
protected Q_SLOTS:
void new_proj() override;
void newContext(Context *ctx);
void open_lpf();
- void open_base();
void save_config();
private:
QAction *actionLoadLPF;
- QAction *actionLoadBase;
QAction *actionSaveConfig;
-
- ArchArgs chipArgs;
-
- std::string currentBaseConfig;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc index 54d1f2c8..f616ca1a 100644 --- a/gui/generic/mainwindow.cc +++ b/gui/generic/mainwindow.cc @@ -26,9 +26,10 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : BaseMainWindow(std::move(context), args, parent)
+MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : BaseMainWindow(std::move(context), handler, parent)
{
+ initMainResource();
QMessageBox::critical(0, "Error - FIXME", "No GUI support for nextpnr-generic");
std::exit(1);
}
diff --git a/gui/generic/mainwindow.h b/gui/generic/mainwindow.h index bb6a4cf1..4d1cf598 100644 --- a/gui/generic/mainwindow.h +++ b/gui/generic/mainwindow.h @@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow Q_OBJECT
public:
- explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index f270b112..ccff2117 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -35,8 +35,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : BaseMainWindow(std::move(context), args, parent)
+MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : BaseMainWindow(std::move(context), handler, parent)
{
initMainResource();
@@ -123,7 +123,7 @@ void MainWindow::new_proj() bool ok;
QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
if (ok && !item.isEmpty()) {
-
+ ArchArgs chipArgs;
chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(item);
QString package = QInputDialog::getItem(this, "Select package", "Package:", getSupportedPackages(chipArgs.type),
@@ -156,7 +156,7 @@ void MainWindow::load_pcf(std::string filename) void MainWindow::newContext(Context *ctx)
{
- std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + chipArgs.package + " )";
+ std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + ctx->archArgs().package + " )";
setWindowTitle(title.c_str());
}
@@ -186,13 +186,12 @@ void MainWindow::onDisableActions() actionSaveAsc->setEnabled(false);
}
-void MainWindow::onJsonLoaded() { actionLoadPCF->setEnabled(true); }
-void MainWindow::onRouteFinished() { actionSaveAsc->setEnabled(true); }
-
-void MainWindow::onProjectLoaded()
+void MainWindow::onUpdateActions()
{
- if (ctx->settings.find(ctx->id("input/pcf")) != ctx->settings.end())
- actionLoadPCF->setEnabled(false);
+ if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
+ actionLoadPCF->setEnabled(true);
+ if (ctx->settings.find(ctx->id("route")) != ctx->settings.end())
+ actionSaveAsc->setEnabled(true);
}
NEXTPNR_NAMESPACE_END
diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h index 4a9a7d8e..6e725a16 100644 --- a/gui/ice40/mainwindow.h +++ b/gui/ice40/mainwindow.h @@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow Q_OBJECT
public:
- explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
@@ -39,9 +39,7 @@ class MainWindow : public BaseMainWindow void load_pcf(std::string filename);
void onDisableActions() override;
- void onJsonLoaded() override;
- void onRouteFinished() override;
- void onProjectLoaded() override;
+ void onUpdateActions() override;
protected Q_SLOTS:
void new_proj() override;
diff --git a/gui/resources/save_json.png b/gui/resources/save_json.png Binary files differnew file mode 100644 index 00000000..cbeb3243 --- /dev/null +++ b/gui/resources/save_json.png diff --git a/gui/worker.cc b/gui/worker.cc index 900883d4..bd14771b 100644 --- a/gui/worker.cc +++ b/gui/worker.cc @@ -68,7 +68,7 @@ void Worker::budget(double freq) { Q_EMIT taskStarted(); try { - ctx->target_freq = freq; + ctx->settings[ctx->id("target_freq")] = std::to_string(freq); assign_budget(ctx); Q_EMIT budget_finish(true); } catch (WorkerInterruptionRequested) { @@ -80,7 +80,7 @@ void Worker::place(bool timing_driven) { Q_EMIT taskStarted(); try { - ctx->timing_driven = timing_driven; + ctx->settings[ctx->id("timing_driven")] = std::to_string(timing_driven); Q_EMIT place_finished(ctx->place()); } catch (WorkerInterruptionRequested) { Q_EMIT taskCanceled(); diff --git a/ice40/arch.cc b/ice40/arch.cc index 80e1fb4c..0b1d280c 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -683,16 +683,24 @@ bool Arch::place() } else { log_error("iCE40 architecture does not support placer '%s'\n", placer.c_str()); } + bool retVal = true; if (bool_or_default(settings, id("opt_timing"), false)) { TimingOptCfg tocfg(getCtx()); tocfg.cellTypes.insert(id_ICESTORM_LC); - return timing_opt(getCtx(), tocfg); - } else { - return true; + retVal = timing_opt(getCtx(), tocfg); } + getCtx()->settings[getCtx()->id("place")] = "1"; + archInfoToAttributes(); + return retVal; } -bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } +bool Arch::route() +{ + bool retVal = router1(getCtx(), Router1Cfg(getCtx())); + getCtx()->settings[getCtx()->id("route")] = "1"; + archInfoToAttributes(); + return retVal; +} // ----------------------------------------------------------------------- diff --git a/ice40/main.cc b/ice40/main.cc index 9b79a08c..83cb04b0 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -36,7 +36,7 @@ class Ice40CommandHandler : public CommandHandler public: Ice40CommandHandler(int argc, char **argv); virtual ~Ice40CommandHandler(){}; - std::unique_ptr<Context> createContext() override; + std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override; void setupArchContext(Context *ctx) override; void validate() override; void customAfterLoad(Context *ctx) override; @@ -116,8 +116,10 @@ void Ice40CommandHandler::setupArchContext(Context *ctx) } } -std::unique_ptr<Context> Ice40CommandHandler::createContext() +std::unique_ptr<Context> Ice40CommandHandler::createContext(std::unordered_map<std::string, Property> &values) { + ArchArgs chipArgs; + chipArgs.type = ArchArgs::NONE; if (vm.count("lp384")) { chipArgs.type = ArchArgs::LP384; chipArgs.package = "qn32"; @@ -153,6 +155,49 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext() chipArgs.package = "sg48"; } + if (vm.count("package")) + chipArgs.package = vm["package"].as<std::string>(); + + if (values.find("arch.name") != values.end()) { + std::string arch_name = values["arch.name"].str; + if (arch_name != "ice40") + log_error("Unsuported architecture '%s'.\n", arch_name.c_str()); + } + if (values.find("arch.type") != values.end()) { + std::string arch_type = values["arch.type"].str; + if (chipArgs.type != ArchArgs::NONE) + log_error("Overriding architecture is unsuported.\n"); + + if (arch_type == "lp384") { + chipArgs.type = ArchArgs::LP384; + } + if (arch_type == "lp1k") { + chipArgs.type = ArchArgs::LP1K; + } + if (arch_type == "lp8k") { + chipArgs.type = ArchArgs::LP8K; + } + if (arch_type == "hx1k") { + chipArgs.type = ArchArgs::HX1K; + } + if (arch_type == "hx8k") { + chipArgs.type = ArchArgs::HX8K; + } + if (arch_type == "up5k") { + chipArgs.type = ArchArgs::UP5K; + } + if (arch_type == "u4k") { + chipArgs.type = ArchArgs::U4K; + } + if (chipArgs.type == ArchArgs::NONE) + log_error("Unsuported FPGA type '%s'.\n", arch_type.c_str()); + } + if (values.find("arch.package") != values.end()) { + if (vm.count("package")) + log_error("Overriding architecture is unsuported.\n"); + chipArgs.package = values["arch.package"].str; + } + if (chipArgs.type == ArchArgs::NONE) { chipArgs.type = ArchArgs::HX1K; chipArgs.package = "tq144"; @@ -163,11 +208,11 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext() } #endif - if (vm.count("package")) - chipArgs.package = vm["package"].as<std::string>(); - auto ctx = std::unique_ptr<Context>(new Context(chipArgs)); + for (auto &val : values) + ctx->settings[ctx->id(val.first)] = val.second; + ctx->settings[ctx->id("arch.package")] = ctx->archArgs().package; if (vm.count("promote-logic")) ctx->settings[ctx->id("promote_logic")] = "1"; if (vm.count("no-promote-globals")) diff --git a/ice40/pack.cc b/ice40/pack.cc index f520b295..d1366c9c 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -341,6 +341,11 @@ static void pack_constants(Context *ctx) gnd_net->driver.port = ctx->id("O"); gnd_cell->ports.at(ctx->id("O")).net = gnd_net.get(); + NetInfo *gnd_net_info = gnd_net.get(); + if (ctx->nets.find(ctx->id("$PACKER_GND_NET")) != ctx->nets.end()) { + gnd_net_info = ctx->nets.find(ctx->id("$PACKER_GND_NET"))->second.get(); + } + std::unique_ptr<CellInfo> vcc_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_VCC"); vcc_cell->params[ctx->id("LUT_INIT")] = "1"; std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo); @@ -349,6 +354,11 @@ static void pack_constants(Context *ctx) vcc_net->driver.port = ctx->id("O"); vcc_cell->ports.at(ctx->id("O")).net = vcc_net.get(); + NetInfo *vcc_net_info = vcc_net.get(); + if (ctx->nets.find(ctx->id("$PACKER_VCC_NET")) != ctx->nets.end()) { + vcc_net_info = ctx->nets.find(ctx->id("$PACKER_VCC_NET"))->second.get(); + } + std::vector<IdString> dead_nets; bool gnd_used = false; @@ -357,26 +367,28 @@ static void pack_constants(Context *ctx) NetInfo *ni = net.second; if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) { IdString drv_cell = ni->driver.cell->name; - set_net_constant(ctx, ni, gnd_net.get(), false); + set_net_constant(ctx, ni, gnd_net_info, false); gnd_used = true; dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) { IdString drv_cell = ni->driver.cell->name; - set_net_constant(ctx, ni, vcc_net.get(), true); + set_net_constant(ctx, ni, vcc_net_info, true); dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); } } - if (gnd_used) { + if (gnd_used && (gnd_net_info == gnd_net.get())) { ctx->cells[gnd_cell->name] = std::move(gnd_cell); ctx->nets[gnd_net->name] = std::move(gnd_net); } // Vcc cell always inserted for now, as it may be needed during carry legalisation (TODO: trim later if actually // never used?) - ctx->cells[vcc_cell->name] = std::move(vcc_cell); - ctx->nets[vcc_net->name] = std::move(vcc_net); + if (vcc_net_info == vcc_net.get()) { + ctx->cells[vcc_cell->name] = std::move(vcc_cell); + ctx->nets[vcc_net->name] = std::move(vcc_net); + } for (auto dn : dead_nets) { ctx->nets.erase(dn); @@ -1224,13 +1236,14 @@ static void pack_special(Context *ctx) } auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")]; - std::string fbp_value = feedback_path == "DELAY" - ? "0" - : feedback_path == "SIMPLE" - ? "1" - : feedback_path == "PHASE_AND_DELAY" - ? "2" - : feedback_path == "EXTERNAL" ? "6" : feedback_path; + std::string fbp_value = + feedback_path == "DELAY" + ? "0" + : feedback_path == "SIMPLE" + ? "1" + : feedback_path == "PHASE_AND_DELAY" + ? "2" + : feedback_path == "EXTERNAL" ? "6" : std::string(feedback_path); if (!std::all_of(fbp_value.begin(), fbp_value.end(), isdigit)) log_error("PLL '%s' has unsupported FEEDBACK_PATH value '%s'\n", ci->name.c_str(ctx), feedback_path.c_str()); @@ -1435,6 +1448,8 @@ bool Arch::pack() ctx->assignArchInfo(); constrain_chains(ctx); ctx->assignArchInfo(); + ctx->settings[ctx->id("pack")] = "1"; + archInfoToAttributes(); log_info("Checksum: 0x%08x\n", ctx->checksum()); return true; } catch (log_execution_error_exception) { diff --git a/ice40/pcf.cc b/ice40/pcf.cc index 15132800..a854a780 100644 --- a/ice40/pcf.cc +++ b/ice40/pcf.cc @@ -119,7 +119,7 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in) } } } - ctx->settings.emplace(ctx->id("input/pcf"), filename); + ctx->settings[ctx->id("input/pcf")] = filename; return true; } catch (log_execution_error_exception) { return false; diff --git a/ice40/project.cc b/ice40/project.cc deleted file mode 100644 index bbd82fd7..00000000 --- a/ice40/project.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "project.h" -#include <boost/filesystem/convenience.hpp> -#include <fstream> -#include "log.h" -#include "pcf.h" - -NEXTPNR_NAMESPACE_BEGIN - -void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) -{ - root.put("project.arch.package", ctx->archArgs().package); - if (ctx->settings.find(ctx->id("input/pcf")) != ctx->settings.end()) { - std::string fn = ctx->settings[ctx->id("input/pcf")]; - root.put("project.input.pcf", make_relative(fn, path).string()); - } -} - -std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root) -{ - ArchArgs chipArgs; - std::string arch_type = root.get<std::string>("project.arch.type"); - if (arch_type == "lp384") { - chipArgs.type = ArchArgs::LP384; - } - if (arch_type == "lp1k") { - chipArgs.type = ArchArgs::LP1K; - } - if (arch_type == "lp8k") { - chipArgs.type = ArchArgs::LP8K; - } - if (arch_type == "hx1k") { - chipArgs.type = ArchArgs::HX1K; - } - if (arch_type == "hx8k") { - chipArgs.type = ArchArgs::HX8K; - } - if (arch_type == "up5k") { - chipArgs.type = ArchArgs::UP5K; - } - if (arch_type == "u4k") { - chipArgs.type = ArchArgs::U4K; - } - chipArgs.package = root.get<std::string>("project.arch.package"); - - return std::unique_ptr<Context>(new Context(chipArgs)); -} - -void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) -{ - auto input = root.get_child("project").get_child("input"); - boost::filesystem::path pcf = boost::filesystem::path(path) / input.get<std::string>("pcf"); - std::ifstream f(pcf.string()); - if (!apply_pcf(ctx, input.get<std::string>("pcf"), f)) - log_error("Loading PCF failed.\n"); -} - -NEXTPNR_NAMESPACE_END diff --git a/json/jsonparse.cc b/json/jsonparse.cc index d463d8ce..caedbb5b 100644 --- a/json/jsonparse.cc +++ b/json/jsonparse.cc @@ -314,7 +314,7 @@ bool is_blackbox(JsonNode *node) } void json_import_cell_params(Context *ctx, string &modname, CellInfo *cell, JsonNode *param_node, - std::unordered_map<IdString, std::string> *dest, int param_id) + std::unordered_map<IdString, Property> *dest, int param_id) { // JsonNode *param; @@ -324,9 +324,9 @@ void json_import_cell_params(Context *ctx, string &modname, CellInfo *cell, Json pId = ctx->id(param_node->data_dict_keys[param_id]); if (param->type == 'N') { - (*dest)[pId] = std::to_string(param->data_number); + (*dest)[pId].setNumber(param->data_number); } else if (param->type == 'S') - (*dest)[pId] = param->data_string; + (*dest)[pId].setString(param->data_string); else log_error("JSON parameter type of \"%s\' of cell \'%s\' not supported\n", pId.c_str(ctx), cell->name.c_str(ctx)); @@ -337,6 +337,49 @@ void json_import_cell_params(Context *ctx, string &modname, CellInfo *cell, Json pId.c_str(ctx), cell->params[pId].c_str(), cell->name.c_str(ctx), modname.c_str()); } +void json_import_net_attrib(Context *ctx, string &modname, NetInfo *net, JsonNode *param_node, + std::unordered_map<IdString, Property> *dest, int param_id) +{ + // + JsonNode *param; + IdString pId; + // + param = param_node->data_dict.at(param_node->data_dict_keys[param_id]); + + pId = ctx->id(param_node->data_dict_keys[param_id]); + if (param->type == 'N') { + (*dest)[pId].setNumber(param->data_number); + } else if (param->type == 'S') + (*dest)[pId].setString(param->data_string); + else + log_error("JSON parameter type of \"%s\' of net \'%s\' not supported\n", pId.c_str(ctx), net->name.c_str(ctx)); + if (json_debug) + log_info(" Added parameter \'%s\'=%s to net \'%s\' " + "of module \'%s\'\n", + pId.c_str(ctx), net->attrs[pId].c_str(), net->name.c_str(ctx), modname.c_str()); +} + +void json_import_top_attrib(Context *ctx, string &modname, JsonNode *param_node, + std::unordered_map<IdString, Property> *dest, int param_id) +{ + // + JsonNode *param; + IdString pId; + // + param = param_node->data_dict.at(param_node->data_dict_keys[param_id]); + + pId = ctx->id(param_node->data_dict_keys[param_id]); + if (param->type == 'N') { + (*dest)[pId].setNumber(param->data_number); + } else if (param->type == 'S') + (*dest)[pId].setString(param->data_string); + else + log_error("JSON parameter type of \"%s\' of module not supported\n", pId.c_str(ctx)); + if (json_debug) + log_info(" Added parameter \'%s\'=%s module \'%s\'\n", pId.c_str(ctx), (*dest)[pId].c_str(), + modname.c_str()); +} + static int const_net_idx = 0; template <typename F> @@ -625,62 +668,70 @@ static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, const string // During packing, this generic IO buffer will be converted to an // architecure primitive. // - std::unique_ptr<CellInfo> iobuf = std::unique_ptr<CellInfo>(new CellInfo()); - iobuf->name = ctx->id(name); - std::copy(net->attrs.begin(), net->attrs.end(), std::inserter(iobuf->attrs, iobuf->attrs.begin())); - if (type == PORT_IN) { - if (ctx->verbose) - log_info("processing input port %s\n", name.c_str()); - iobuf->type = ctx->id("$nextpnr_ibuf"); - iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT}; - // Special case: input, etc, directly drives inout - if (net->driver.cell != nullptr) { - if (net->driver.cell->type != ctx->id("$nextpnr_iobuf")) - log_error("Top-level input '%s' also driven by %s.%s.\n", name.c_str(), - net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx)); - net = net->driver.cell->ports.at(ctx->id("I")).net; - } - assert(net->driver.cell == nullptr); - net->driver.port = ctx->id("O"); - net->driver.cell = iobuf.get(); - } else if (type == PORT_OUT) { - if (ctx->verbose) - log_info("processing output port %s\n", name.c_str()); - iobuf->type = ctx->id("$nextpnr_obuf"); - iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), net, PORT_IN}; - PortRef ref; - ref.cell = iobuf.get(); - ref.port = ctx->id("I"); - net->users.push_back(ref); - } else if (type == PORT_INOUT) { - if (ctx->verbose) - log_info("processing inout port %s\n", name.c_str()); - iobuf->type = ctx->id("$nextpnr_iobuf"); - iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), nullptr, PORT_IN}; - - // Split the input and output nets for bidir ports - std::unique_ptr<NetInfo> net2 = std::unique_ptr<NetInfo>(new NetInfo()); - net2->name = ctx->id("$" + net->name.str(ctx) + "$iobuf_i"); - net2->driver = net->driver; - if (net->driver.cell != nullptr) { - net2->driver.cell->ports[net2->driver.port].net = net2.get(); - net->driver.cell = nullptr; + if (ctx->settings.find(ctx->id("synth")) == ctx->settings.end()) { + std::unique_ptr<CellInfo> iobuf = std::unique_ptr<CellInfo>(new CellInfo()); + iobuf->name = ctx->id(name); + std::copy(net->attrs.begin(), net->attrs.end(), std::inserter(iobuf->attrs, iobuf->attrs.begin())); + if (type == PORT_IN) { + if (ctx->verbose) + log_info("processing input port %s\n", name.c_str()); + iobuf->type = ctx->id("$nextpnr_ibuf"); + iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT}; + // Special case: input, etc, directly drives inout + if (net->driver.cell != nullptr) { + if (net->driver.cell->type != ctx->id("$nextpnr_iobuf")) + log_error("Top-level input '%s' also driven by %s.%s.\n", name.c_str(), + net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx)); + net = net->driver.cell->ports.at(ctx->id("I")).net; + } + assert(net->driver.cell == nullptr); + net->driver.port = ctx->id("O"); + net->driver.cell = iobuf.get(); + } else if (type == PORT_OUT) { + if (ctx->verbose) + log_info("processing output port %s\n", name.c_str()); + iobuf->type = ctx->id("$nextpnr_obuf"); + iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), net, PORT_IN}; + PortRef ref; + ref.cell = iobuf.get(); + ref.port = ctx->id("I"); + net->users.push_back(ref); + } else if (type == PORT_INOUT) { + if (ctx->verbose) + log_info("processing inout port %s\n", name.c_str()); + iobuf->type = ctx->id("$nextpnr_iobuf"); + iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), nullptr, PORT_IN}; + + // Split the input and output nets for bidir ports + std::unique_ptr<NetInfo> net2 = std::unique_ptr<NetInfo>(new NetInfo()); + net2->name = ctx->id("$" + net->name.str(ctx) + "$iobuf_i"); + net2->driver = net->driver; + if (net->driver.cell != nullptr) { + net2->driver.cell->ports[net2->driver.port].net = net2.get(); + net->driver.cell = nullptr; + } + iobuf->ports[ctx->id("I")].net = net2.get(); + PortRef ref; + ref.cell = iobuf.get(); + ref.port = ctx->id("I"); + net2->users.push_back(ref); + ctx->nets[net2->name] = std::move(net2); + + iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT}; + assert(net->driver.cell == nullptr); + net->driver.port = ctx->id("O"); + net->driver.cell = iobuf.get(); + } else { + assert(false); } - iobuf->ports[ctx->id("I")].net = net2.get(); - PortRef ref; - ref.cell = iobuf.get(); - ref.port = ctx->id("I"); - net2->users.push_back(ref); - ctx->nets[net2->name] = std::move(net2); - - iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT}; - assert(net->driver.cell == nullptr); - net->driver.port = ctx->id("O"); - net->driver.cell = iobuf.get(); - } else { - assert(false); + ctx->cells[iobuf->name] = std::move(iobuf); } - ctx->cells[iobuf->name] = std::move(iobuf); + + PortInfo pinfo; + pinfo.name = net->name; + pinfo.net = net; + pinfo.type = type; + ctx->ports[net->name] = pinfo; } void json_import_toplevel_port(Context *ctx, const string &modname, const std::vector<IdString> &netnames, @@ -711,6 +762,11 @@ void json_import(Context *ctx, string modname, JsonNode *node) return; log_info("Importing module %s\n", modname.c_str()); + ctx->attrs[ctx->id("module")] = modname; + JsonNode *attr_node = node->data_dict.at("attributes"); + for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) { + json_import_top_attrib(ctx, modname, attr_node, &ctx->attrs, attrid); + } JsonNode *ports_parent = nullptr; if (node->data_dict.count("ports") > 0) @@ -825,7 +881,43 @@ void json_import(Context *ctx, string modname, JsonNode *node) json_import_toplevel_port(ctx, modname, netids, ports_parent->data_dict_keys[portid], here); } } + if (node->data_dict.count("netnames")) { + JsonNode *net_parent = node->data_dict.at("netnames"); + for (int nnid = 0; nnid < GetSize(net_parent->data_dict_keys); nnid++) { + JsonNode *here; + + here = net_parent->data_dict.at(net_parent->data_dict_keys[nnid]); + std::string basename = net_parent->data_dict_keys[nnid]; + if (here->data_dict.count("bits")) { + JsonNode *bits = here->data_dict.at("bits"); + assert(bits->type == 'A'); + size_t num_bits = bits->data_array.size(); + for (size_t i = 0; i < num_bits; i++) { + std::string name = + basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(i) + std::string("]")); + IdString net_id = ctx->id(name); + if (here->data_dict.count("attributes") && ctx->nets.find(net_id) != ctx->nets.end()) { + NetInfo *this_net = ctx->nets[net_id].get(); + + JsonNode *attr_node = here->data_dict.at("attributes"); + if (attr_node->type != 'D') + log_error("JSON attribute list of \'%s\' is not a data dictionary\n", + this_net->name.c_str(ctx)); + + // + // Loop through all attributes, adding them into the + // design to annotate the cell + // + for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) { + json_import_net_attrib(ctx, modname, this_net, attr_node, &this_net->attrs, attrid); + } + } + } + } + } + } check_all_nets_driven(ctx); + ctx->settings[ctx->id("synth")] = "1"; } }; // End Namespace JsonParser @@ -856,7 +948,55 @@ bool parse_json_file(std::istream &f, std::string &filename, Context *ctx) log_info("Checksum: 0x%08x\n", ctx->checksum()); log_break(); - ctx->settings.emplace(ctx->id("input/json"), filename); + ctx->attributesToArchInfo(); + return true; + } catch (log_execution_error_exception) { + return false; + } +} + +bool load_json_settings(std::istream &f, std::string &filename, std::unordered_map<std::string, Property> &values) +{ + try { + using namespace JsonParser; + + if (!f) + log_error("failed to open JSON file.\n"); + + int lineno = 1; + + JsonNode root(f, lineno); + + if (root.type != 'D') + log_error("JSON root node is not a dictionary.\n"); + + if (root.data_dict.count("modules") != 0) { + JsonNode *modules = root.data_dict.at("modules"); + + if (modules->type != 'D') + log_error("JSON modules node is not a dictionary.\n"); + + for (auto &it : modules->data_dict) { + JsonNode *node = it.second; + if (is_blackbox(node)) + continue; + + if (node->data_dict.count("settings")) { + JsonNode *attr_node = node->data_dict.at("settings"); + for (int attrid = 0; attrid < GetSize(attr_node->data_dict_keys); attrid++) { + JsonNode *param = attr_node->data_dict.at(attr_node->data_dict_keys[attrid]); + std::string pId = attr_node->data_dict_keys[attrid]; + if (param->type == 'N') { + values[pId].setNumber(param->data_number); + } else if (param->type == 'S') + values[pId].setString(param->data_string); + else + log_error("JSON parameter type of \"%s\' of module not supported\n", pId.c_str()); + } + } + } + } + return true; } catch (log_execution_error_exception) { return false; diff --git a/json/jsonparse.h b/json/jsonparse.h index fe71444f..65e3f02e 100644 --- a/json/jsonparse.h +++ b/json/jsonparse.h @@ -27,7 +27,8 @@ NEXTPNR_NAMESPACE_BEGIN extern bool parse_json_file(std::istream &, std::string &, Context *); - +extern bool load_json_settings(std::istream &f, std::string &filename, + std::unordered_map<std::string, Property> &values); NEXTPNR_NAMESPACE_END #endif diff --git a/json/jsonwrite.cc b/json/jsonwrite.cc new file mode 100644 index 00000000..0b7a5b25 --- /dev/null +++ b/json/jsonwrite.cc @@ -0,0 +1,180 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "jsonwrite.h" +#include <assert.h> +#include <fstream> +#include <iostream> +#include <iterator> +#include <log.h> +#include <map> +#include <string> +#include "nextpnr.h" +#include "version.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace JsonWriter { + +std::string get_string(std::string str) +{ + std::string newstr = "\""; + for (char c : str) { + if (c == '\\') + newstr += c; + newstr += c; + } + return newstr + "\""; +} + +std::string get_name(IdString name, Context *ctx) { return get_string(name.c_str(ctx)); } + +void write_parameters(std::ostream &f, Context *ctx, const std::unordered_map<IdString, Property> ¶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 diff --git a/generic/project.cc b/json/jsonwrite.h index 342fcac3..9240bc96 100644 --- a/generic/project.cc +++ b/json/jsonwrite.h @@ -17,21 +17,17 @@ * */ -#include "project.h" -#include <boost/filesystem/convenience.hpp> -#include <fstream> -#include "log.h" +#ifndef JSON_WRITER +#define JSON_WRITER -NEXTPNR_NAMESPACE_BEGIN - -void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) {} +#include <ostream> +#include <string> +#include "nextpnr.h" -std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root) -{ - ArchArgs chipArgs; - return std::unique_ptr<Context>(new Context(chipArgs)); -} +NEXTPNR_NAMESPACE_BEGIN -void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {} +extern bool write_json_file(std::ostream &, std::string &, Context *); NEXTPNR_NAMESPACE_END + +#endif |