Merge pull request #284 from YosysHQ/json_write

Initial support for writing to json files from nextpnr.
This commit is contained in:
David Shah 2019-07-03 12:39:38 +01:00 committed by GitHub
commit 8f2813279c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1044 additions and 806 deletions

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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());

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
// ---------------------------------------------------------------

View File

@ -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));
}

View File

@ -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;

View File

@ -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>

View File

@ -29,17 +29,17 @@
#include "designwidget.h"
#include "fpgaviewwidget.h"
#include "jsonparse.h"
#include "jsonwrite.h"
#include "log.h"
#include "mainwindow.h"
#include "project.h"
#include "pythontab.h"
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
NEXTPNR_NAMESPACE_BEGIN
BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
: QMainWindow(parent), chipArgs(args), ctx(std::move(context)), timing_driven(false)
BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
: QMainWindow(parent), handler(handler), ctx(std::move(context)), timing_driven(false)
{
initBasenameResource();
qRegisterMetaType<std::string>();
@ -130,25 +130,6 @@ void BaseMainWindow::writeInfo(std::string text) { console->info(text); }
void BaseMainWindow::createMenusAndBars()
{
// File menu / project toolbar actions
actionNew = new QAction("New", this);
actionNew->setIcon(QIcon(":/icons/resources/new.png"));
actionNew->setShortcuts(QKeySequence::New);
actionNew->setStatusTip("New project file");
connect(actionNew, &QAction::triggered, this, &BaseMainWindow::new_proj);
actionOpen = new QAction("Open", this);
actionOpen->setIcon(QIcon(":/icons/resources/open.png"));
actionOpen->setShortcuts(QKeySequence::Open);
actionOpen->setStatusTip("Open an existing project file");
connect(actionOpen, &QAction::triggered, this, &BaseMainWindow::open_proj);
actionSave = new QAction("Save", this);
actionSave->setIcon(QIcon(":/icons/resources/save.png"));
actionSave->setShortcuts(QKeySequence::Save);
actionSave->setStatusTip("Save existing project to disk");
actionSave->setEnabled(false);
connect(actionSave, &QAction::triggered, this, &BaseMainWindow::save_proj);
QAction *actionExit = new QAction("Exit", this);
actionExit->setIcon(QIcon(":/icons/resources/exit.png"));
actionExit->setShortcuts(QKeySequence::Quit);
@ -158,13 +139,26 @@ void BaseMainWindow::createMenusAndBars()
// Help menu actions
QAction *actionAbout = new QAction("About", this);
// Design menu options
// Gile menu options
actionNew = new QAction("New", this);
actionNew->setIcon(QIcon(":/icons/resources/new.png"));
actionNew->setShortcuts(QKeySequence::New);
actionNew->setStatusTip("New project");
connect(actionNew, &QAction::triggered, this, &BaseMainWindow::new_proj);
actionLoadJSON = new QAction("Open JSON", this);
actionLoadJSON->setIcon(QIcon(":/icons/resources/open_json.png"));
actionLoadJSON->setStatusTip("Open an existing JSON file");
actionLoadJSON->setEnabled(true);
connect(actionLoadJSON, &QAction::triggered, this, &BaseMainWindow::open_json);
actionSaveJSON = new QAction("Save JSON", this);
actionSaveJSON->setIcon(QIcon(":/icons/resources/save_json.png"));
actionSaveJSON->setStatusTip("Write to JSON file");
actionSaveJSON->setEnabled(true);
connect(actionSaveJSON, &QAction::triggered, this, &BaseMainWindow::save_json);
// Design menu options
actionPack = new QAction("Pack", this);
actionPack->setIcon(QIcon(":/icons/resources/pack.png"));
actionPack->setStatusTip("Pack current design");
@ -244,13 +238,12 @@ void BaseMainWindow::createMenusAndBars()
// Add File menu actions
menuFile->addAction(actionNew);
menuFile->addAction(actionOpen);
menuFile->addAction(actionSave);
menuFile->addAction(actionLoadJSON);
menuFile->addAction(actionSaveJSON);
menuFile->addSeparator();
menuFile->addAction(actionExit);
// Add Design menu actions
menuDesign->addAction(actionLoadJSON);
menuDesign->addAction(actionPack);
menuDesign->addAction(actionAssignBudget);
menuDesign->addAction(actionPlace);
@ -261,17 +254,13 @@ void BaseMainWindow::createMenusAndBars()
// Add Help menu actions
menuHelp->addAction(actionAbout);
// Project toolbar
QToolBar *projectToolBar = new QToolBar("Project");
addToolBar(Qt::TopToolBarArea, projectToolBar);
projectToolBar->addAction(actionNew);
projectToolBar->addAction(actionOpen);
projectToolBar->addAction(actionSave);
// Main action bar
mainActionBar = new QToolBar("Main");
addToolBar(Qt::TopToolBarArea, mainActionBar);
mainActionBar->addAction(actionNew);
mainActionBar->addAction(actionLoadJSON);
mainActionBar->addAction(actionSaveJSON);
mainActionBar->addSeparator();
mainActionBar->addAction(actionPack);
mainActionBar->addAction(actionAssignBudget);
mainActionBar->addAction(actionPlace);
@ -304,25 +293,29 @@ void BaseMainWindow::createMenusAndBars()
setStatusBar(statusBar);
}
void BaseMainWindow::load_json(std::string filename)
{
disableActions();
std::ifstream f(filename);
if (parse_json_file(f, filename, ctx.get())) {
log("Loading design successful.\n");
Q_EMIT updateTreeView();
updateLoaded();
} else {
actionLoadJSON->setEnabled(true);
log("Loading design failed.\n");
}
}
void BaseMainWindow::open_json()
{
QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
if (!fileName.isEmpty()) {
load_json(fileName.toStdString());
disableActions();
ctx = handler->load_json(fileName.toStdString());
Q_EMIT contextChanged(ctx.get());
Q_EMIT updateTreeView();
log("Loading design successful.\n");
updateActions();
}
}
void BaseMainWindow::save_json()
{
QString fileName = QFileDialog::getSaveFileName(this, QString("Save JSON"), QString(), QString("*.json"));
if (!fileName.isEmpty()) {
std::string fn = fileName.toStdString();
std::ofstream f(fn);
if (write_json_file(f, fn, ctx.get()))
log("Saving JSON successful.\n");
else
log("Saving JSON failed.\n");
}
}
@ -332,9 +325,7 @@ void BaseMainWindow::pack_finished(bool status)
if (status) {
log("Packing design successful.\n");
Q_EMIT updateTreeView();
actionPlace->setEnabled(true);
actionAssignBudget->setEnabled(true);
onPackFinished();
updateActions();
} else {
log("Packing design failed.\n");
}
@ -345,8 +336,7 @@ void BaseMainWindow::budget_finish(bool status)
disableActions();
if (status) {
log("Assigning timing budget successful.\n");
actionPlace->setEnabled(true);
onBudgetFinished();
updateActions();
} else {
log("Assigning timing budget failed.\n");
}
@ -358,8 +348,7 @@ void BaseMainWindow::place_finished(bool status)
if (status) {
log("Placing design successful.\n");
Q_EMIT updateTreeView();
actionRoute->setEnabled(true);
onPlaceFinished();
updateActions();
} else {
log("Placing design failed.\n");
}
@ -370,7 +359,7 @@ void BaseMainWindow::route_finished(bool status)
if (status) {
log("Routing design successful.\n");
Q_EMIT updateTreeView();
onRouteFinished();
updateActions();
} else
log("Routing design failed.\n");
}
@ -386,9 +375,6 @@ void BaseMainWindow::taskStarted()
disableActions();
actionPause->setEnabled(true);
actionStop->setEnabled(true);
actionNew->setEnabled(false);
actionOpen->setEnabled(false);
}
void BaseMainWindow::taskPaused()
@ -396,9 +382,6 @@ void BaseMainWindow::taskPaused()
disableActions();
actionPlay->setEnabled(true);
actionStop->setEnabled(true);
actionNew->setEnabled(false);
actionOpen->setEnabled(false);
}
void BaseMainWindow::budget()
@ -416,52 +399,32 @@ void BaseMainWindow::place() { Q_EMIT task->place(timing_driven); }
void BaseMainWindow::disableActions()
{
actionLoadJSON->setEnabled(false);
actionLoadJSON->setEnabled(true);
actionPack->setEnabled(false);
actionAssignBudget->setEnabled(false);
actionPlace->setEnabled(false);
actionRoute->setEnabled(false);
actionExecutePy->setEnabled(true);
actionPlay->setEnabled(false);
actionPause->setEnabled(false);
actionStop->setEnabled(false);
actionNew->setEnabled(true);
actionOpen->setEnabled(true);
if (ctx->settings.find(ctx->id("input/json")) != ctx->settings.end())
actionSave->setEnabled(true);
else
actionSave->setEnabled(false);
onDisableActions();
}
void BaseMainWindow::updateLoaded()
void BaseMainWindow::updateActions()
{
disableActions();
actionPack->setEnabled(true);
onJsonLoaded();
onProjectLoaded();
}
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::projectLoad(std::string filename)
{
ProjectHandler proj;
disableActions();
ctx = proj.load(filename);
Q_EMIT contextChanged(ctx.get());
log_info("Loaded project %s...\n", filename.c_str());
updateLoaded();
}
void BaseMainWindow::open_proj()
{
QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj"));
if (!fileName.isEmpty()) {
projectLoad(fileName.toStdString());
}
onUpdateActions();
}
void BaseMainWindow::execute_python()
@ -473,18 +436,5 @@ void BaseMainWindow::execute_python()
}
void BaseMainWindow::notifyChangeContext() { Q_EMIT contextChanged(ctx.get()); }
void BaseMainWindow::save_proj()
{
if (currentProj.empty()) {
QString fileName = QFileDialog::getSaveFileName(this, QString("Save Project"), QString(), QString("*.proj"));
if (fileName.isEmpty())
return;
currentProj = fileName.toStdString();
}
if (!currentProj.empty()) {
ProjectHandler proj;
proj.save(ctx.get(), currentProj);
}
}
NEXTPNR_NAMESPACE_END

View File

@ -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;

View File

@ -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
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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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:

View File

@ -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

View File

@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow
Q_OBJECT
public:
explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
@ -39,9 +39,7 @@ class MainWindow : public BaseMainWindow
void load_pcf(std::string filename);
void onDisableActions() override;
void onJsonLoaded() override;
void onRouteFinished() override;
void onProjectLoaded() override;
void onUpdateActions() override;
protected Q_SLOTS:
void new_proj() override;

BIN
gui/resources/save_json.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -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();

View File

@ -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;
}
// -----------------------------------------------------------------------

View File

@ -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"))

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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};
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);
// 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("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;

View File

@ -27,7 +27,8 @@
NEXTPNR_NAMESPACE_BEGIN
extern bool parse_json_file(std::istream &, std::string &, Context *);
extern bool load_json_settings(std::istream &f, std::string &filename,
std::unordered_map<std::string, Property> &values);
NEXTPNR_NAMESPACE_END
#endif

180
json/jsonwrite.cc Normal file
View File

@ -0,0 +1,180 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "jsonwrite.h"
#include <assert.h>
#include <fstream>
#include <iostream>
#include <iterator>
#include <log.h>
#include <map>
#include <string>
#include "nextpnr.h"
#include "version.h"
NEXTPNR_NAMESPACE_BEGIN
namespace JsonWriter {
std::string get_string(std::string str)
{
std::string newstr = "\"";
for (char c : str) {
if (c == '\\')
newstr += c;
newstr += c;
}
return newstr + "\"";
}
std::string get_name(IdString name, Context *ctx) { return get_string(name.c_str(ctx)); }
void write_parameters(std::ostream &f, Context *ctx, const std::unordered_map<IdString, Property> &parameters,
bool for_module = false)
{
bool first = true;
for (auto &param : parameters) {
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first, ctx).c_str());
if (param.second.isString())
f << get_string(param.second);
else
f << param.second.num;
first = false;
}
}
void write_module(std::ostream &f, Context *ctx)
{
auto val = ctx->attrs.find(ctx->id("module"));
if (val != ctx->attrs.end())
f << stringf(" %s: {\n", get_string(val->second.str).c_str());
else
f << stringf(" %s: {\n", get_string("top").c_str());
f << stringf(" \"settings\": {");
write_parameters(f, ctx, ctx->settings, true);
f << stringf("\n },\n");
f << stringf(" \"attributes\": {");
write_parameters(f, ctx, ctx->attrs, true);
f << stringf("\n },\n");
f << stringf(" \"ports\": {");
bool first = true;
for (auto &pair : ctx->ports) {
auto &c = pair.second;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(c.name, ctx).c_str());
f << stringf(" \"direction\": \"%s\",\n",
c.type == PORT_IN ? "input" : c.type == PORT_INOUT ? "inout" : "output");
f << stringf(" \"bits\": [ %d ]\n", pair.first.index);
f << stringf(" }");
first = false;
}
f << stringf("\n },\n");
f << stringf(" \"cells\": {");
first = true;
for (auto &pair : ctx->cells) {
auto &c = pair.second;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(c->name, ctx).c_str());
f << stringf(" \"hide_name\": %s,\n", c->name.c_str(ctx)[0] == '$' ? "1" : "0");
f << stringf(" \"type\": %s,\n", get_name(c->type, ctx).c_str());
f << stringf(" \"parameters\": {");
write_parameters(f, ctx, c->params);
f << stringf("\n },\n");
f << stringf(" \"attributes\": {");
write_parameters(f, ctx, c->attrs);
f << stringf("\n },\n");
f << stringf(" \"port_directions\": {");
bool first2 = true;
for (auto &conn : c->ports) {
auto &p = conn.second;
std::string direction = (p.type == PORT_IN) ? "input" : (p.type == PORT_OUT) ? "output" : "inout";
f << stringf("%s\n", first2 ? "" : ",");
f << stringf(" %s: \"%s\"", get_name(conn.first, ctx).c_str(), direction.c_str());
first2 = false;
}
f << stringf("\n },\n");
f << stringf(" \"connections\": {");
first2 = true;
for (auto &conn : c->ports) {
auto &p = conn.second;
f << stringf("%s\n", first2 ? "" : ",");
if (p.net)
f << stringf(" %s: [ %d ]", get_name(conn.first, ctx).c_str(), p.net->name.index);
else
f << stringf(" %s: [ ]", get_name(conn.first, ctx).c_str());
first2 = false;
}
f << stringf("\n }\n");
f << stringf(" }");
first = false;
}
f << stringf("\n },\n");
f << stringf(" \"netnames\": {");
first = true;
for (auto &pair : ctx->nets) {
auto &w = pair.second;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(w->name, ctx).c_str());
f << stringf(" \"hide_name\": %s,\n", w->name.c_str(ctx)[0] == '$' ? "1" : "0");
f << stringf(" \"bits\": [ %d ] ,\n", pair.first.index);
f << stringf(" \"attributes\": {");
write_parameters(f, ctx, w->attrs);
f << stringf("\n }\n");
f << stringf(" }");
first = false;
}
f << stringf("\n }\n");
f << stringf(" }");
}
void write_context(std::ostream &f, Context *ctx)
{
f << stringf("{\n");
f << stringf(" \"creator\": %s,\n",
get_string("Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")").c_str());
f << stringf(" \"modules\": {\n");
write_module(f, ctx);
f << stringf("\n }");
f << stringf("\n}\n");
}
}; // End Namespace JsonWriter
bool write_json_file(std::ostream &f, std::string &filename, Context *ctx)
{
try {
using namespace JsonWriter;
if (!f)
log_error("failed to open JSON file.\n");
write_context(f, ctx);
log_break();
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

View File

@ -17,21 +17,17 @@
*
*/
#include "project.h"
#include <boost/filesystem/convenience.hpp>
#include <fstream>
#include "log.h"
#ifndef JSON_WRITER
#define JSON_WRITER
#include <ostream>
#include <string>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) {}
std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
{
ArchArgs chipArgs;
return std::unique_ptr<Context>(new Context(chipArgs));
}
void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {}
extern bool write_json_file(std::ostream &, std::string &, Context *);
NEXTPNR_NAMESPACE_END
#endif