diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/command.cc | 123 | ||||
-rw-r--r-- | common/command.h | 10 | ||||
-rw-r--r-- | common/nextpnr.cc | 116 | ||||
-rw-r--r-- | common/nextpnr.h | 75 | ||||
-rw-r--r-- | common/place_common.cc | 2 | ||||
-rw-r--r-- | common/placer1.cc | 24 | ||||
-rw-r--r-- | common/placer1.h | 4 | ||||
-rw-r--r-- | common/placer_heap.cc | 12 | ||||
-rw-r--r-- | common/placer_heap.h | 4 | ||||
-rw-r--r-- | common/project.cc | 144 | ||||
-rw-r--r-- | common/project.h | 45 | ||||
-rw-r--r-- | common/pybindings.cc | 2 | ||||
-rw-r--r-- | common/router1.cc | 10 | ||||
-rw-r--r-- | common/router1.h | 5 | ||||
-rw-r--r-- | common/settings.h | 74 | ||||
-rw-r--r-- | common/timing.cc | 20 | ||||
-rw-r--r-- | common/timing_opt.h | 6 |
17 files changed, 319 insertions, 357 deletions
diff --git a/common/command.cc b/common/command.cc index 3eafdb17..dc98d032 100644 --- a/common/command.cc +++ b/common/command.cc @@ -28,6 +28,7 @@ #endif #include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string.hpp> #include <boost/filesystem/convenience.hpp> #include <boost/program_options.hpp> #include <fstream> @@ -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..b5908e3f 100644 --- a/common/command.h +++ b/common/command.h @@ -22,9 +22,9 @@ #define COMMAND_H #include <boost/program_options.hpp> +#include <fstream> #include "nextpnr.h" -#include "project.h" -#include "settings.h" +#include "log.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..a026b60d 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -19,6 +19,7 @@ #include "nextpnr.h" #include "log.h" +#include <boost/algorithm/string.hpp> NEXTPNR_NAMESPACE_BEGIN @@ -453,4 +454,119 @@ 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..6950ac76 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,41 @@ 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 +331,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 +364,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 +542,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 +688,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 +704,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 +724,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..a2272b83 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 && ctx->setting<int>("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 (ctx->setting<int>("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 && ctx->setting<int>("slack_redist_iter") > 0 && iter % ctx->setting<int>("slack_redist_iter") == 0) { assign_budget(ctx, true /* quiet */); } // Invoke timing analysis to obtain criticalities - if (!cfg.budgetBased && ctx->timing_driven) + if (!cfg.budgetBased && ctx->setting<bool>("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 (ctx->setting<bool>("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 (ctx->setting<bool>("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 (ctx->setting<bool>("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,12 +1131,12 @@ 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(); } diff --git a/common/placer1.h b/common/placer1.h index 4c7c7339..0f2e2894 100644 --- a/common/placer1.h +++ b/common/placer1.h @@ -20,11 +20,11 @@ #define PLACE_H #include "nextpnr.h" -#include "settings.h" +#include "log.h" NEXTPNR_NAMESPACE_BEGIN -struct Placer1Cfg : public Settings +struct Placer1Cfg { Placer1Cfg(Context *ctx); float constraintWeight; diff --git a/common/placer_heap.cc b/common/placer_heap.cc index f9b639f8..7b72200a 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 (ctx->setting<bool>("timing_driven")) get_criticalities(ctx, &net_crit); if (legal_hpwl < best_hpwl) { @@ -1516,11 +1516,11 @@ 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); } NEXTPNR_NAMESPACE_END @@ -1538,7 +1538,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..2def5e75 100644 --- a/common/placer_heap.h +++ b/common/placer_heap.h @@ -27,11 +27,11 @@ #ifndef PLACER_HEAP_H #define PLACER_HEAP_H #include "nextpnr.h" -#include "settings.h" +#include "log.h" NEXTPNR_NAMESPACE_BEGIN -struct PlacerHeapCfg : public Settings +struct PlacerHeapCfg { PlacerHeapCfg(Context *ctx); 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..f3d325ad 100644 --- a/common/router1.h +++ b/common/router1.h @@ -21,11 +21,10 @@ #define ROUTER1_H #include "nextpnr.h" -#include "settings.h" - +#include "log.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..60aef3fa 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,17 @@ 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 +684,13 @@ 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 +897,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..cdc02406 100644 --- a/common/timing_opt.h +++ b/common/timing_opt.h @@ -18,13 +18,13 @@ */ #include "nextpnr.h" -#include "settings.h" +#include "log.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 |