From 3bb9a7df01a03a31be87da719e0ee4bd4c38d5fd Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 5 Aug 2018 16:13:34 +0200 Subject: Added command parser and common implementation --- common/command.cc | 246 ++++++++++++++++++++++++++++ common/command.h | 68 ++++++++ ecp5/main.cc | 231 ++++++++------------------- generic/main.cc | 136 ++++------------ ice40/main.cc | 466 +++++++++++++----------------------------------------- 5 files changed, 517 insertions(+), 630 deletions(-) create mode 100644 common/command.cc create mode 100644 common/command.h diff --git a/common/command.cc b/common/command.cc new file mode 100644 index 00000000..aae7ed36 --- /dev/null +++ b/common/command.cc @@ -0,0 +1,246 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 NO_GUI +#include +#include "application.h" +#include "mainwindow.h" +#endif +#ifndef NO_PYTHON +#include "pybindings.h" +#endif + +#include +#include +#include +#include +#include "command.h" +#include "design_utils.h" +#include "jsonparse.h" +#include "log.h" +#include "version.h" + +NEXTPNR_NAMESPACE_BEGIN + +CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) { log_files.push_back(stdout); } + +bool CommandHandler::parseOptions() +{ + options.add(getGeneralOptions()).add(getArchOptions()); + try { + po::parsed_options parsed = + po::command_line_parser(argc, argv) + .style(po::command_line_style::default_style ^ po::command_line_style::allow_guessing) + .options(options) + .positional(pos) + .run(); + po::store(parsed, vm); + po::notify(vm); + return true; + } catch (std::exception &e) { + std::cout << e.what() << "\n"; + return false; + } +} + +bool CommandHandler::executeBeforeContext() +{ + if (vm.count("help") || argc == 1) { + std::cout << boost::filesystem::basename(argv[0]) + << " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n"; + std::cout << options << "\n"; + return argc != 1; + } + + if (vm.count("version")) { + std::cout << boost::filesystem::basename(argv[0]) + << " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n"; + return true; + } + validate(); + return false; +} + +po::options_description CommandHandler::getGeneralOptions() +{ + po::options_description general("General options"); + general.add_options()("help,h", "show help"); + general.add_options()("verbose,v", "verbose output"); + general.add_options()("debug", "debug output"); + general.add_options()("force,f", "keep running after errors"); +#ifndef NO_GUI + general.add_options()("gui", "start gui"); +#endif +#ifndef NO_PYTHON + general.add_options()("run", po::value>(), "python file to execute"); + pos.add("run", -1); +#endif + general.add_options()("json", po::value(), "JSON design file to ingest"); + general.add_options()("seed", po::value(), "seed value for random number generator"); + general.add_options()("slack_redist_iter", po::value(), "number of iterations between slack redistribution"); + general.add_options()("cstrweight", po::value(), "placer weighting for relative constraint satisfaction"); + + general.add_options()("version,V", "show version"); + general.add_options()("test", "check architecture database integrity"); + general.add_options()("freq", po::value(), "set target frequency for design in MHz"); + general.add_options()("no-tmdriv", "disable timing-driven placement"); + general.add_options()("save", po::value(), "project file to write"); + general.add_options()("load", po::value(), "project file to read"); + return general; +} + +void CommandHandler::setupContext(Context *ctx) +{ + if (vm.count("verbose")) { + ctx->verbose = true; + } + + if (vm.count("debug")) { + ctx->verbose = true; + ctx->debug = true; + } + + if (vm.count("force")) { + ctx->force = true; + } + + if (vm.count("seed")) { + ctx->rngseed(vm["seed"].as()); + } + + if (vm.count("slack_redist_iter")) { + ctx->slack_redist_iter = vm["slack_redist_iter"].as(); + if (vm.count("freq") && vm["freq"].as() == 0) { + ctx->auto_freq = true; +#ifndef NO_GUI + if (!vm.count("gui")) +#endif + log_warning("Target frequency not specified. Will optimise for max frequency.\n"); + } + } + + if (vm.count("cstrweight")) { + // ctx->placer_constraintWeight = vm["cstrweight"].as(); + } + + if (vm.count("freq")) { + auto freq = vm["freq"].as(); + if (freq > 0) + ctx->target_freq = freq * 1e6; + } + + ctx->timing_driven = true; + if (vm.count("no-tmdriv")) + ctx->timing_driven = false; +} + +int CommandHandler::executeMain(std::unique_ptr ctx) +{ + if (vm.count("test")) { + ctx->archcheck(); + return 0; + } + +#ifndef NO_GUI + if (vm.count("gui")) { + Application a(argc, argv); + MainWindow w(std::move(ctx), chipArgs); + try { + if (vm.count("json")) { + std::string filename = vm["json"].as(); + std::ifstream f(filename); + if (!parse_json_file(f, filename, w.getContext())) + log_error("Loading design failed.\n"); + + customAfterLoad(w.getContext()); + } + } catch (log_execution_error_exception) { + // show error is handled by gui itself + } + w.show(); + + return a.exec(); + } +#endif + if (vm.count("json")) { + std::string filename = vm["json"].as(); + std::ifstream f(filename); + if (!parse_json_file(f, filename, ctx.get())) + log_error("Loading design failed.\n"); + + customAfterLoad(ctx.get()); + + if (!ctx->pack() && !ctx->force) + log_error("Packing design failed.\n"); + ctx->check(); + print_utilisation(ctx.get()); + if (!vm.count("pack-only")) { + if (!ctx->place() && !ctx->force) + log_error("Placing design failed.\n"); + ctx->check(); + if (!ctx->route() && !ctx->force) + log_error("Routing design failed.\n"); + } + + customBitstream(ctx.get()); + } + +#ifndef NO_PYTHON + if (vm.count("run")) { + init_python(argv[0], true); + python_export_global("ctx", *ctx); + + std::vector files = vm["run"].as>(); + for (auto filename : files) + execute_python_file(filename.c_str()); + + deinit_python(); + } +#endif + return 0; +} + +void CommandHandler::conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, + const char *opt2) +{ + if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted()) { + std::string msg = "Conflicting options '" + std::string(opt1) + "' and '" + std::string(opt2) + "'."; + log_error("%s\n", msg.c_str()); + } +} + +int CommandHandler::exec() +{ + try { + if (!parseOptions()) + return -1; + + if (executeBeforeContext()) + return 0; + std::unique_ptr ctx = createContext(); + setupContext(ctx.get()); + setupArchContext(ctx.get()); + return executeMain(std::move(ctx)); + } catch (log_execution_error_exception) { + return -1; + } +} + +NEXTPNR_NAMESPACE_END diff --git a/common/command.h b/common/command.h new file mode 100644 index 00000000..e6595f6a --- /dev/null +++ b/common/command.h @@ -0,0 +1,68 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 COMMAND_H +#define COMMAND_H + +#include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace po = boost::program_options; + +class CommandHandler +{ + public: + CommandHandler(int argc, char **argv); + virtual ~CommandHandler(){}; + + int exec(); + + protected: + virtual void setupArchContext(Context *ctx) = 0; + virtual std::unique_ptr createContext() = 0; + virtual po::options_description getArchOptions() = 0; + virtual void validate(){}; + virtual void customAfterLoad(Context *ctx){}; + virtual void customBitstream(Context *ctx){}; + void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2); + + private: + bool parseOptions(); + bool executeBeforeContext(); + void setupContext(Context *ctx); + int executeMain(std::unique_ptr ctx); + po::options_description getGeneralOptions(); + + protected: + po::variables_map vm; + ArchArgs chipArgs; + + private: + po::options_description options; + po::positional_options_description pos; + int argc; + char **argv; +}; + +NEXTPNR_NAMESPACE_END + +#endif // COMMAND_H diff --git a/ecp5/main.cc b/ecp5/main.cc index dde3ffed..5ad5a9bf 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -19,191 +19,84 @@ #ifdef MAIN_EXECUTABLE -#ifndef NO_GUI -#include -#include "application.h" -#include "mainwindow.h" -#endif -#ifndef NO_PYTHON -#include "pybindings.h" -#endif -#include -#include #include -#include - -#include "log.h" -#include "nextpnr.h" -#include "version.h" - #include "bitstream.h" +#include "command.h" #include "design_utils.h" -#include "jsonparse.h" +#include "log.h" #include "timing.h" USING_NEXTPNR_NAMESPACE -int main(int argc, char *argv[]) +class ECP5CommandHandler : public CommandHandler { - try { - - namespace po = boost::program_options; - int rc = 0; - - log_files.push_back(stdout); - - po::options_description options("Allowed options"); - options.add_options()("help,h", "show help"); - options.add_options()("verbose,v", "verbose output"); - options.add_options()("force,f", "keep running after errors"); -#ifndef NO_GUI - options.add_options()("gui", "start gui"); -#endif - options.add_options()("test", "check architecture database integrity"); - - options.add_options()("25k", "set device type to LFE5U-25F"); - options.add_options()("45k", "set device type to LFE5U-45F"); - options.add_options()("85k", "set device type to LFE5U-85F"); - - options.add_options()("package", po::value(), "select device package (defaults to CABGA381)"); - - options.add_options()("json", po::value(), "JSON design file to ingest"); - options.add_options()("seed", po::value(), "seed value for random number generator"); - - options.add_options()("basecfg", po::value(), "base chip configuration in Trellis text format"); - options.add_options()("textcfg", po::value(), "textual configuration in Trellis format to write"); - - po::positional_options_description pos; -#ifndef NO_PYTHON - options.add_options()("run", po::value>(), "python file to execute"); - pos.add("run", -1); -#endif - options.add_options()("version,V", "show version"); - - po::variables_map vm; - try { - po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run(); - - po::store(parsed, vm); - - po::notify(vm); - } - - catch (std::exception &e) { - std::cout << e.what() << "\n"; - return 1; - } - - if (vm.count("help") || argc == 1) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; - std::cout << "\n"; - std::cout << options << "\n"; - return argc != 1; - } + public: + ECP5CommandHandler(int argc, char **argv); + virtual ~ECP5CommandHandler(){}; + std::unique_ptr createContext() override; + void setupArchContext(Context *ctx) override{}; + void validate() override; + void customBitstream(Context *ctx) override; - if (vm.count("version")) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; - return 1; - } + protected: + po::options_description getArchOptions(); +}; - ArchArgs args; - args.type = ArchArgs::LFE5U_45F; +ECP5CommandHandler::ECP5CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} - if (vm.count("25k")) - args.type = ArchArgs::LFE5U_25F; - if (vm.count("45k")) - args.type = ArchArgs::LFE5U_45F; - if (vm.count("85k")) - args.type = ArchArgs::LFE5U_85F; - if (vm.count("package")) - args.package = vm["package"].as(); - else - args.package = "CABGA381"; - args.speed = 6; - std::unique_ptr ctx = std::unique_ptr(new Context(args)); - - if (vm.count("verbose")) { - ctx->verbose = true; - } - - if (vm.count("force")) { - ctx->force = true; - } - - if (vm.count("seed")) { - ctx->rngseed(vm["seed"].as()); - } - - ctx->timing_driven = true; - if (vm.count("no-tmdriv")) - ctx->timing_driven = false; - - if (vm.count("test")) - ctx->archcheck(); - -#ifndef NO_GUI - if (vm.count("gui")) { - Application a(argc, argv); - MainWindow w(std::move(ctx),args); - w.show(); - - return a.exec(); - } -#endif - if (vm.count("json")) { - std::string filename = vm["json"].as(); - std::ifstream f(filename); - if (!parse_json_file(f, filename, ctx.get())) - log_error("Loading design failed.\n"); - - if (!ctx->pack() && !ctx->force) - log_error("Packing design failed.\n"); - if (vm.count("freq")) - ctx->target_freq = vm["freq"].as() * 1e6; - assign_budget(ctx.get()); - ctx->check(); - print_utilisation(ctx.get()); - - if (!ctx->place() && !ctx->force) - log_error("Placing design failed.\n"); - ctx->check(); - if (!ctx->route() && !ctx->force) - log_error("Routing design failed.\n"); +po::options_description ECP5CommandHandler::getArchOptions() +{ + po::options_description specific("Architecture specific options"); + specific.add_options()("25k", "set device type to LFE5U-25F"); + specific.add_options()("45k", "set device type to LFE5U-45F"); + specific.add_options()("85k", "set device type to LFE5U-85F"); + specific.add_options()("package", po::value(), "select device package (defaults to CABGA381)"); + specific.add_options()("basecfg", po::value(), "base chip configuration in Trellis text format"); + specific.add_options()("textcfg", po::value(), "textual configuration in Trellis format to write"); + return specific; +} +void ECP5CommandHandler::validate() +{ + if ((vm.count("25k") + vm.count("45k") + vm.count("85k")) > 1) + log_error("Only one device type can be set\n"); +} - std::string basecfg; - if (vm.count("basecfg")) - basecfg = vm["basecfg"].as(); +void ECP5CommandHandler::customBitstream(Context *ctx) +{ + std::string basecfg; + if (vm.count("basecfg")) + basecfg = vm["basecfg"].as(); - std::string textcfg; - if (vm.count("textcfg")) - textcfg = vm["textcfg"].as(); - write_bitstream(ctx.get(), basecfg, textcfg); - } + std::string textcfg; + if (vm.count("textcfg")) + textcfg = vm["textcfg"].as(); -#ifndef NO_PYTHON - if (vm.count("run")) { - init_python(argv[0], true); - python_export_global("ctx", ctx); + write_bitstream(ctx, basecfg, textcfg); +} - std::vector files = vm["run"].as>(); - for (auto filename : files) - execute_python_file(filename.c_str()); +std::unique_ptr ECP5CommandHandler::createContext() +{ + chipArgs.type = ArchArgs::LFE5U_45F; + + if (vm.count("25k")) + chipArgs.type = ArchArgs::LFE5U_25F; + if (vm.count("45k")) + chipArgs.type = ArchArgs::LFE5U_45F; + if (vm.count("85k")) + chipArgs.type = ArchArgs::LFE5U_85F; + if (vm.count("package")) + chipArgs.package = vm["package"].as(); + else + chipArgs.package = "CABGA381"; + chipArgs.speed = 6; + + return std::unique_ptr(new Context(chipArgs)); +} - deinit_python(); - } -#endif - return rc; - } catch (log_execution_error_exception) { -#if defined(_MSC_VER) - _exit(EXIT_FAILURE); -#else - _Exit(EXIT_FAILURE); -#endif - } +int main(int argc, char *argv[]) +{ + ECP5CommandHandler handler(argc, argv); + return handler.exec(); } #endif diff --git a/generic/main.cc b/generic/main.cc index 8653da01..412a28ac 100644 --- a/generic/main.cc +++ b/generic/main.cc @@ -19,123 +19,47 @@ #ifdef MAIN_EXECUTABLE -#ifndef NO_GUI -#include -#include "application.h" -#include "mainwindow.h" -#endif -#ifndef NO_PYTHON -#include "pybindings.h" -#endif -#include -#include -#include +#include +#include "command.h" +#include "design_utils.h" #include "log.h" -#include "nextpnr.h" -#include "version.h" +#include "timing.h" USING_NEXTPNR_NAMESPACE -int main(int argc, char *argv[]) +class GenericCommandHandler : public CommandHandler { - try { - - namespace po = boost::program_options; - int rc = 0; - - log_files.push_back(stdout); - - po::options_description options("Allowed options"); - options.add_options()("help,h", "show help"); - options.add_options()("verbose,v", "verbose output"); - options.add_options()("force,f", "keep running after errors"); -#ifndef NO_GUI - options.add_options()("gui", "start gui"); -#endif - - po::positional_options_description pos; -#ifndef NO_PYTHON - options.add_options()("run", po::value>(), "python file to execute"); - pos.add("run", -1); -#endif - options.add_options()("version,V", "show version"); - - po::variables_map vm; - try { - po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run(); - - po::store(parsed, vm); - - po::notify(vm); - } - - catch (std::exception &e) { - std::cout << e.what() << "\n"; - return 1; - } - - if (vm.count("help") || argc == 1) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; - std::cout << "\n"; - std::cout << options << "\n"; - return argc != 1; - } + public: + GenericCommandHandler(int argc, char **argv); + virtual ~GenericCommandHandler(){}; + std::unique_ptr createContext() override; + void setupArchContext(Context *ctx) override{}; + void customBitstream(Context *ctx) override; - if (vm.count("version")) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; - return 1; - } + protected: + po::options_description getArchOptions(); +}; - ArchArgs chipArgs{}; - std::unique_ptr ctx = std::unique_ptr(new Context(chipArgs)); +GenericCommandHandler::GenericCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} - if (vm.count("verbose")) { - ctx->verbose = true; - } - - if (vm.count("force")) { - ctx->force = true; - } - - if (vm.count("seed")) { - ctx->rngseed(vm["seed"].as()); - } - -#ifndef NO_GUI - if (vm.count("gui")) { - Application a(argc, argv); - MainWindow w(std::move(ctx), chipArgs); - w.show(); - - return a.exec(); - } -#endif - -#ifndef NO_PYTHON - if (vm.count("run")) { - init_python(argv[0], true); - python_export_global("ctx", *ctx.get()); +po::options_description GenericCommandHandler::getArchOptions() +{ + po::options_description specific("Architecture specific options"); + specific.add_options()("generic", "set device type to generic"); + return specific; +} - std::vector files = vm["run"].as>(); - for (auto filename : files) - execute_python_file(filename.c_str()); +void GenericCommandHandler::customBitstream(Context *ctx) { log_error("Here is when bitstream gets created"); } - deinit_python(); - } -#endif +std::unique_ptr GenericCommandHandler::createContext() +{ + return std::unique_ptr(new Context(chipArgs)); +} - return rc; - } catch (log_execution_error_exception) { -#if defined(_MSC_VER) - _exit(EXIT_FAILURE); -#else - _Exit(EXIT_FAILURE); -#endif - } +int main(int argc, char *argv[]) +{ + GenericCommandHandler handler(argc, argv); + return handler.exec(); } #endif diff --git a/ice40/main.cc b/ice40/main.cc index 358bf3c5..a98ca07e 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Miodrag Milanovic * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,390 +20,145 @@ #ifdef MAIN_EXECUTABLE -#ifndef NO_GUI -#include -#include "application.h" -#include "mainwindow.h" -#endif -#ifndef NO_PYTHON -#include "pybindings.h" -#endif - -#include -#include -#include -#include #include -#include #include "bitstream.h" +#include "command.h" #include "design_utils.h" #include "jsonparse.h" #include "log.h" -#include "nextpnr.h" #include "pcf.h" #include "timing.h" -#include "version.h" USING_NEXTPNR_NAMESPACE -void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2) +class Ice40CommandHandler : public CommandHandler { - if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted()) { - std::string msg = "Conflicting options '" + std::string(opt1) + "' and '" + std::string(opt1) + "'."; - log_error("%s\n", msg.c_str()); - } -} - -int main(int argc, char *argv[]) + public: + Ice40CommandHandler(int argc, char **argv); + virtual ~Ice40CommandHandler(){}; + std::unique_ptr createContext() override; + void setupArchContext(Context *ctx) override; + void validate() override; + void customAfterLoad(Context *ctx) override; + void customBitstream(Context *ctx) override; + + protected: + po::options_description getArchOptions(); +}; + +Ice40CommandHandler::Ice40CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} + +po::options_description Ice40CommandHandler::getArchOptions() { - try { - namespace po = boost::program_options; - namespace pt = boost::property_tree; - int rc = 0; - std::string str; - - log_files.push_back(stdout); - - po::options_description options("Allowed options"); - options.add_options()("help,h", "show help"); - options.add_options()("verbose,v", "verbose output"); - options.add_options()("debug", "debug output"); - options.add_options()("force,f", "keep running after errors"); -#ifndef NO_GUI - options.add_options()("gui", "start gui"); -#endif - options.add_options()("pack-only", "pack design only without placement or routing"); - - po::positional_options_description pos; -#ifndef NO_PYTHON - options.add_options()("run", po::value>(), "python file to execute"); - pos.add("run", -1); -#endif - options.add_options()("json", po::value(), "JSON design file to ingest"); - options.add_options()("pcf", po::value(), "PCF constraints file to ingest"); - options.add_options()("asc", po::value(), "asc bitstream file to write"); - options.add_options()("read", po::value(), "asc bitstream file to read"); - options.add_options()("seed", po::value(), "seed value for random number generator"); - options.add_options()("slack_redist_iter", po::value(), - "number of iterations between slack redistribution"); - options.add_options()("cstrweight", po::value(), - "placer weighting for relative constraint satisfaction"); - - options.add_options()("version,V", "show version"); - options.add_options()("tmfuzz", "run path delay estimate fuzzer"); - options.add_options()("test", "check architecture database integrity"); + po::options_description specific("Architecture specific options"); #ifdef ICE40_HX1K_ONLY - options.add_options()("hx1k", "set device type to iCE40HX1K"); + specific.add_options()("hx1k", "set device type to iCE40HX1K"); #else - options.add_options()("lp384", "set device type to iCE40LP384"); - options.add_options()("lp1k", "set device type to iCE40LP1K"); - options.add_options()("lp8k", "set device type to iCE40LP8K"); - options.add_options()("hx1k", "set device type to iCE40HX1K"); - options.add_options()("hx8k", "set device type to iCE40HX8K"); - options.add_options()("up5k", "set device type to iCE40UP5K"); + specific.add_options()("lp384", "set device type to iCE40LP384"); + specific.add_options()("lp1k", "set device type to iCE40LP1K"); + specific.add_options()("lp8k", "set device type to iCE40LP8K"); + specific.add_options()("hx1k", "set device type to iCE40HX1K"); + specific.add_options()("hx8k", "set device type to iCE40HX8K"); + specific.add_options()("up5k", "set device type to iCE40UP5K"); #endif - options.add_options()("freq", po::value(), "set target frequency for design in MHz"); - options.add_options()("no-tmdriv", "disable timing-driven placement"); - options.add_options()("package", po::value(), "set device package"); - options.add_options()("save", po::value(), "project file to write"); - options.add_options()("load", po::value(), "project file to read"); - - po::variables_map vm; - try { - po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run(); - - po::store(parsed, vm); - - po::notify(vm); - } catch (std::exception &e) { - std::cout << e.what() << "\n"; - return 1; - } - - conflicting_options(vm, "read", "json"); -#ifndef ICE40_HX1K_ONLY - if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") + - vm.count("up5k")) > 1) - log_error("Only one device type can be set\n"); -#endif - if (vm.count("help") || argc == 1) { - help: - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; - std::cout << "\n"; - std::cout << options << "\n"; - return argc != 1; - } - - if (vm.count("version")) { - std::cout << boost::filesystem::basename(argv[0]) - << " -- Next Generation Place and Route (git " - "sha1 " GIT_COMMIT_HASH_STR ")\n"; - return 1; - } - - if (vm.count("load")) { - try { - pt::ptree root; - std::string filename = vm["load"].as(); - pt::read_json(filename, root); - log_info("Loading project %s...\n", filename.c_str()); - log_break(); - - bool isLoadingGui = vm.count("gui") > 0; - std::string ascOutput; - if (vm.count("asc")) - ascOutput = vm["asc"].as(); - vm.clear(); - - int version = root.get("project.version"); - if (version != 1) - log_error("Wrong project format version.\n"); - - std::string arch_name = root.get("project.arch.name"); - if (arch_name != "ice40") - log_error("Unsuported project architecture.\n"); - - std::string arch_type = root.get("project.arch.type"); - vm.insert(std::make_pair(arch_type, po::variable_value())); - - std::string arch_package = root.get("project.arch.package"); - vm.insert(std::make_pair("package", po::variable_value(arch_package, false))); + specific.add_options()("package", po::value(), "set device package"); + specific.add_options()("pcf", po::value(), "PCF constraints file to ingest"); + specific.add_options()("asc", po::value(), "asc bitstream file to write"); + specific.add_options()("read", po::value(), "asc bitstream file to read"); + specific.add_options()("tmfuzz", "run path delay estimate fuzzer"); + specific.add_options()("pack-only", "pack design only without placement or routing"); + return specific; +} +void Ice40CommandHandler::validate() +{ + conflicting_options(vm, "read", "json"); + if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") + + vm.count("up5k")) > 1) + log_error("Only one device type can be set\n"); +} - auto project = root.get_child("project"); - if (project.count("input")) { - auto input = project.get_child("input"); - if (input.count("json")) - vm.insert(std::make_pair("json", po::variable_value(input.get("json"), false))); - if (input.count("pcf")) - vm.insert(std::make_pair("pcf", po::variable_value(input.get("pcf"), false))); - } - if (project.count("params")) { - auto params = project.get_child("params"); - if (params.count("freq")) - vm.insert(std::make_pair("freq", po::variable_value(params.get("freq"), false))); - if (params.count("seed")) - vm.insert(std::make_pair("seed", po::variable_value(params.get("seed"), false))); - } - if (!ascOutput.empty()) - vm.insert(std::make_pair("asc", po::variable_value(ascOutput, false))); - if (isLoadingGui) - vm.insert(std::make_pair("gui", po::variable_value())); - po::notify(vm); - } catch (...) { - log_error("Error loading project file.\n"); - } - } +void Ice40CommandHandler::customAfterLoad(Context *ctx) +{ + if (vm.count("pcf")) { + std::ifstream pcf(vm["pcf"].as()); + if (!apply_pcf(ctx, pcf)) + log_error("Loading PCF failed.\n"); + } +} +void Ice40CommandHandler::customBitstream(Context *ctx) +{ + if (vm.count("asc")) { + std::string filename = vm["asc"].as(); + std::ofstream f(filename); + write_asc(ctx, f); + } +} - ArchArgs chipArgs; +void Ice40CommandHandler::setupArchContext(Context *ctx) +{ + if (vm.count("tmfuzz")) + ice40DelayFuzzerMain(ctx); + + if (vm.count("read")) { + std::string filename = vm["read"].as(); + std::ifstream f(filename); + if (!read_asc(ctx, f)) + log_error("Loading ASC failed.\n"); + } +} - if (vm.count("lp384")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::LP384; - chipArgs.package = "qn32"; - } +std::unique_ptr Ice40CommandHandler::createContext() +{ + if (vm.count("lp384")) { + chipArgs.type = ArchArgs::LP384; + chipArgs.package = "qn32"; + } - if (vm.count("lp1k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::LP1K; - chipArgs.package = "tq144"; - } + if (vm.count("lp1k")) { + chipArgs.type = ArchArgs::LP1K; + chipArgs.package = "tq144"; + } - if (vm.count("lp8k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::LP8K; - chipArgs.package = "ct256"; - } + if (vm.count("lp8k")) { + chipArgs.type = ArchArgs::LP8K; + chipArgs.package = "ct256"; + } - if (vm.count("hx1k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::HX1K; - chipArgs.package = "tq144"; - } + if (vm.count("hx1k")) { + chipArgs.type = ArchArgs::HX1K; + chipArgs.package = "tq144"; + } - if (vm.count("hx8k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::HX8K; - chipArgs.package = "ct256"; - } + if (vm.count("hx8k")) { + chipArgs.type = ArchArgs::HX8K; + chipArgs.package = "ct256"; + } - if (vm.count("up5k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::UP5K; - chipArgs.package = "sg48"; - } + if (vm.count("up5k")) { + chipArgs.type = ArchArgs::UP5K; + chipArgs.package = "sg48"; + } - if (chipArgs.type == ArchArgs::NONE) { - chipArgs.type = ArchArgs::HX1K; - chipArgs.package = "tq144"; - } + if (chipArgs.type == ArchArgs::NONE) { + chipArgs.type = ArchArgs::HX1K; + chipArgs.package = "tq144"; + } #ifdef ICE40_HX1K_ONLY - if (chipArgs.type != ArchArgs::HX1K) { - std::cout << "This version of nextpnr-ice40 is built with " - "HX1K-support " - "only.\n"; - return 1; - } -#endif - - if (vm.count("package")) - chipArgs.package = vm["package"].as(); - - if (vm.count("save")) { - Context ctx(chipArgs); - std::string filename = vm["save"].as(); - std::ofstream f(filename); - pt::ptree root; - 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(chipArgs).c_str(&ctx)); - root.put("project.arch.package", chipArgs.package); - if (vm.count("json")) - root.put("project.input.json", vm["json"].as()); - if (vm.count("pcf")) - root.put("project.input.pcf", vm["pcf"].as()); - if (vm.count("freq")) - root.put("project.params.freq", vm["freq"].as()); - if (vm.count("seed")) - root.put("project.params.seed", vm["seed"].as()); - pt::write_json(f, root); - return 1; - } - - std::unique_ptr ctx = std::unique_ptr(new Context(chipArgs)); - - if (vm.count("verbose")) { - ctx->verbose = true; - } - - if (vm.count("debug")) { - ctx->verbose = true; - ctx->debug = true; - } - - if (vm.count("force")) { - ctx->force = true; - } - - if (vm.count("seed")) { - ctx->rngseed(vm["seed"].as()); - } - - if (vm.count("slack_redist_iter")) { - ctx->slack_redist_iter = vm["slack_redist_iter"].as(); - if (vm.count("freq") && vm["freq"].as() == 0) { - ctx->auto_freq = true; -#ifndef NO_GUI - if (!vm.count("gui")) -#endif - log_warning("Target frequency not specified. Will optimise for max frequency.\n"); - } - } - - if (vm.count("cstrweight")) { - ctx->placer_constraintWeight = vm["cstrweight"].as(); - } - - if (vm.count("test")) - ctx->archcheck(); - - if (vm.count("tmfuzz")) - ice40DelayFuzzerMain(ctx.get()); - - if (vm.count("freq")) { - auto freq = vm["freq"].as(); - if (freq > 0) - ctx->target_freq = freq * 1e6; - } - - ctx->timing_driven = true; - if (vm.count("no-tmdriv")) - ctx->timing_driven = false; - - if (vm.count("read")) { - std::string filename = vm["read"].as(); - std::ifstream f(filename); - if (!read_asc(ctx.get(), f)) - log_error("Loading ASC failed.\n"); - } - -#ifndef NO_GUI - if (vm.count("gui")) { - Application a(argc, argv); - MainWindow w(std::move(ctx), chipArgs); - if (vm.count("json")) { - std::string filename = vm["json"].as(); - std::string pcf = ""; - w.load_json(filename); - if (vm.count("pcf")) { - pcf = vm["pcf"].as(); - w.load_pcf(pcf); - } - } - w.show(); - - return a.exec(); - } + if (chipArgs.type != ArchArgs::HX1K) { + log_error("This version of nextpnr-ice40 is built with HX1K-support only.\n"); + } #endif - if (vm.count("json")) { - std::string filename = vm["json"].as(); - std::ifstream f(filename); - if (!parse_json_file(f, filename, ctx.get())) - log_error("Loading design failed.\n"); - - if (vm.count("pcf")) { - std::ifstream pcf(vm["pcf"].as()); - if (!apply_pcf(ctx.get(), pcf)) - log_error("Loading PCF failed.\n"); - } - if (!ctx->pack() && !ctx->force) - log_error("Packing design failed.\n"); - ctx->check(); - print_utilisation(ctx.get()); - if (!vm.count("pack-only")) { - if (!ctx->place() && !ctx->force) - log_error("Placing design failed.\n"); - ctx->check(); - if (!ctx->route() && !ctx->force) - log_error("Routing design failed.\n"); - } - } + if (vm.count("package")) + chipArgs.package = vm["package"].as(); - if (vm.count("asc")) { - std::string filename = vm["asc"].as(); - std::ofstream f(filename); - write_asc(ctx.get(), f); - } - -#ifndef NO_PYTHON - if (vm.count("run")) { - init_python(argv[0], true); - python_export_global("ctx", *ctx.get()); - - std::vector files = vm["run"].as>(); - for (auto filename : files) - execute_python_file(filename.c_str()); + return std::unique_ptr(new Context(chipArgs)); +} - deinit_python(); - } -#endif - return rc; - } catch (log_execution_error_exception) { -#if defined(_MSC_VER) - _exit(EXIT_FAILURE); -#else - _Exit(EXIT_FAILURE); -#endif - } +int main(int argc, char *argv[]) +{ + Ice40CommandHandler handler(argc, argv); + return handler.exec(); } #endif -- cgit v1.2.3 From 7794bbfb3fbd488ee84231642c79ebbfa8eb8bb3 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 5 Aug 2018 16:13:49 +0200 Subject: Fix message for pcf loading --- ice40/pcf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ice40/pcf.cc b/ice40/pcf.cc index ac1c8598..410fa1c9 100644 --- a/ice40/pcf.cc +++ b/ice40/pcf.cc @@ -31,7 +31,7 @@ bool apply_pcf(Context *ctx, std::istream &in) { try { if (!in) - log_error("failed to open PCF file"); + log_error("failed to open PCF file\n"); std::string line; while (std::getline(in, line)) { size_t cstart = line.find("#"); -- cgit v1.2.3 From 9510c444c93fdb8923d77651562fb698e59dea5f Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 5 Aug 2018 18:02:33 +0200 Subject: Disable menu options on json load --- common/command.cc | 1 + gui/basewindow.cc | 10 ++++++++-- gui/basewindow.h | 6 +++--- gui/ice40/mainwindow.h | 3 ++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/common/command.cc b/common/command.cc index aae7ed36..f144db37 100644 --- a/common/command.cc +++ b/common/command.cc @@ -170,6 +170,7 @@ int CommandHandler::executeMain(std::unique_ptr ctx) log_error("Loading design failed.\n"); customAfterLoad(w.getContext()); + w.updateJsonLoaded(); } } catch (log_execution_error_exception) { // show error is handled by gui itself diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 53720156..98dc9e20 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -302,8 +302,7 @@ void BaseMainWindow::load_json(std::string filename) if (parse_json_file(f, filename, ctx.get())) { log("Loading design successful.\n"); Q_EMIT updateTreeView(); - actionPack->setEnabled(true); - onJsonLoaded(); + updateJsonLoaded(); } else { actionLoadJSON->setEnabled(true); log("Loading design failed.\n"); @@ -425,4 +424,11 @@ void BaseMainWindow::disableActions() onDisableActions(); } +void BaseMainWindow::updateJsonLoaded() +{ + disableActions(); + actionPack->setEnabled(true); + onJsonLoaded(); +} + NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.h b/gui/basewindow.h index 341cc8e2..67f39ac2 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -48,12 +48,12 @@ class BaseMainWindow : public QMainWindow explicit BaseMainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent = 0); virtual ~BaseMainWindow(); Context *getContext() { return ctx.get(); } - - void load_json(std::string filename); - + void updateJsonLoaded(); protected: void createMenusAndBars(); void disableActions(); + void load_json(std::string filename); + virtual void onDisableActions(){}; virtual void onJsonLoaded(){}; virtual void onPackFinished(){}; diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h index 829375e2..c235f0a4 100644 --- a/gui/ice40/mainwindow.h +++ b/gui/ice40/mainwindow.h @@ -34,9 +34,10 @@ class MainWindow : public BaseMainWindow public: void createMenu(); - void load_pcf(std::string filename); protected: + void load_pcf(std::string filename); + void onDisableActions() override; void onJsonLoaded() override; void onRouteFinished() override; -- cgit v1.2.3 From 7aab4925b4573613e9b39c4f141c3e3f1a9e6f57 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 5 Aug 2018 22:31:59 -0700 Subject: Change getBudgetOverride() signature to return bool and modify budget in place --- ice40/arch.cc | 10 ++++++---- ice40/arch.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 0b168383..b8bb13ea 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -637,17 +637,19 @@ std::vector Arch::getGroupGroups(GroupId group) const // ----------------------------------------------------------------------- -delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { const auto &driver = net_info->driver; if (driver.port == id_cout) { auto driver_loc = getBelLocation(driver.cell->bel); auto sink_loc = getBelLocation(sink.cell->bel); if (driver_loc.y == sink_loc.y) - return 0; - return 250; + budget = 0; + else + budget = 190; + return true; } - return budget; + return false; } // ----------------------------------------------------------------------- diff --git a/ice40/arch.h b/ice40/arch.h index 236f73f1..328950df 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -766,7 +766,7 @@ struct Arch : BaseCtx delay_t getRipupDelayPenalty() const { return 200; } float getDelayNS(delay_t v) const { return v * 0.001; } uint32_t getDelayChecksum(delay_t v) const { return v; } - delay_t getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const; + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; // ------------------------------------------------- -- cgit v1.2.3 From 8a6ff4b261b23d8bce3efb0236b6a74621121918 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 5 Aug 2018 22:33:14 -0700 Subject: Modify getBudgetOverride for generic and ecp5 too --- ecp5/arch.cc | 2 +- ecp5/arch.h | 2 +- generic/arch.cc | 2 +- generic/arch.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d72b0085..d2d62241 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -422,7 +422,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const return 200 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y)); } -delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const { return budget; } +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } // ----------------------------------------------------------------------- diff --git a/ecp5/arch.h b/ecp5/arch.h index 74f0c4e1..e00e111a 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -805,7 +805,7 @@ struct Arch : BaseCtx delay_t getRipupDelayPenalty() const { return 200; } float getDelayNS(delay_t v) const { return v * 0.001; } uint32_t getDelayChecksum(delay_t v) const { return v; } - delay_t getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const; + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; // ------------------------------------------------- diff --git a/generic/arch.cc b/generic/arch.cc index 7e65d411..0fa93da8 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -408,7 +408,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const return (dx + dy) * grid_distance_to_delay; } -delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const { return budget; } +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } // --------------------------------------------------------------- diff --git a/generic/arch.h b/generic/arch.h index a5b3470f..59fe8d05 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -200,7 +200,7 @@ struct Arch : BaseCtx delay_t getRipupDelayPenalty() const { return 1.0; } float getDelayNS(delay_t v) const { return v; } uint32_t getDelayChecksum(delay_t v) const { return 0; } - delay_t getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const; + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; bool pack() { return true; } bool place(); -- cgit v1.2.3 From e314ea761abd8f55b3341f733c01d13ce2d4fae5 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 5 Aug 2018 22:38:54 -0700 Subject: WIP for new assign_budget() using topographical ordering --- common/timing.cc | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 6 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index d37a0f59..23fbd9f9 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -23,6 +23,7 @@ #include #include "log.h" #include "util.h" +#include NEXTPNR_NAMESPACE_BEGIN @@ -38,6 +39,14 @@ struct Timing PortRefVector *crit_path; DelayFrequency *slack_histogram; + struct TimingData { + TimingData() : max_arrival(), max_path_length(), min_remaining_budget() {} + TimingData(delay_t max_arrival) : max_arrival(max_arrival), max_path_length(), min_remaining_budget() {} + delay_t max_arrival; + unsigned max_path_length = 0; + delay_t min_remaining_budget; + }; + Timing(Context *ctx, bool update, PortRefVector *crit_path = nullptr, DelayFrequency *slack_histogram = nullptr) : ctx(ctx), update(update), min_slack(1.0e12 / ctx->target_freq), crit_path(crit_path), slack_histogram(slack_histogram) @@ -53,8 +62,8 @@ struct Timing // If budget override is less than existing budget, then do not increment // path length int pl = path_length + 1; - auto budget = ctx->getBudgetOverride(net, usr, net_budget); - if (budget < net_budget) { + auto budget = net_budget; + if (ctx->getBudgetOverride(net, usr, budget) && budget < net_budget) { net_budget = budget; pl = std::max(1, path_length); } @@ -109,16 +118,17 @@ struct Timing delay_t walk_paths() { - delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); + const auto clk_period = delay_t(1.0e12 / ctx->target_freq); // Go through all clocked drivers and distribute the available path // slack evenly into the budget of every sink on the path +#if 0 for (auto &cell : ctx->cells) { for (auto port : cell.second->ports) { if (port.second.type == PORT_OUT) { IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first); if (clock_domain != IdString()) { - delay_t slack = default_slack; // TODO: clock constraints + delay_t slack = clk_period; // TODO: clock constraints DelayInfo clkToQ; if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ)) slack -= clkToQ.maxDelay(); @@ -128,16 +138,165 @@ struct Timing } } } + +#else + std::vector topographical_order; + std::unordered_map port_fanin; + std::unordered_map net_data; + + std::vector input_ports; + std::vector output_ports; + for (auto &cell : ctx->cells) { + input_ports.clear(); + output_ports.clear(); + bool is_io = cell.second->type == ctx->id_sb_io; + for (auto& port : cell.second->ports) { + if (!port.second.net) continue; + if (port.second.type == PORT_OUT) + output_ports.push_back(&port.second); + else + input_ports.push_back(port.first); + } + + for (auto o : output_ports) { + IdString clock_domain = ctx->getPortClock(cell.second.get(), o->name); + if (clock_domain != IdString()) { + DelayInfo clkToQ; + ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); + topographical_order.emplace_back(o->net); + net_data.emplace(o->net, TimingData{ clkToQ.maxDelay() }); + } + else { + if (is_io) { + topographical_order.emplace_back(o->net); + net_data.emplace(o->net, TimingData{}); + } + for (auto i : input_ports) { + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); + if (is_path) + port_fanin[o]++; + } + } + } + } + + std::deque queue(topographical_order.begin(), topographical_order.end()); + + while (!queue.empty()) { + const auto net = queue.front(); + queue.pop_front(); + + for (auto &usr : net->users) { + if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + } else { + // Follow outputs of the user + for (auto& port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto it = port_fanin.find(&port.second); + NPNR_ASSERT(it != port_fanin.end()); + if (--it->second == 0) { + topographical_order.emplace_back(port.second.net); + queue.emplace_back(port.second.net); + port_fanin.erase(it); + } + } + } + } + } + } + } + + // Find the maximum arrival time and max path length for each net + for (auto net : topographical_order) { + auto &nd = net_data.at(net); + const auto net_arrival = nd.max_arrival; + const auto net_length_plus_one = nd.max_path_length + 1; + nd.min_remaining_budget = clk_period; + for (auto &usr : net->users) { + if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + } else { + auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + delay_t budget; + auto budget_override = ctx->getBudgetOverride(net, usr, budget); + auto usr_arrival = net_arrival + net_delay; + // Follow outputs of the user + for (auto port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + DelayInfo comb_delay; + // Look up delay through this path + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto& data = net_data[port.second.net]; + auto& arrival = data.max_arrival; + arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); + if (!budget_override) { + auto& path_length = data.max_path_length; + path_length = std::max(path_length, net_length_plus_one); + } + } + } + } + } + } + } + + for (auto net : boost::adaptors::reverse(topographical_order)) { + auto &nd = net_data.at(net); + const delay_t net_length_plus_one = nd.max_path_length + 1; + auto& net_min_remaining_budget = nd.min_remaining_budget; + for (auto &usr : net->users) { + const auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + auto budget_override = ctx->getBudgetOverride(net, usr, usr.budget); + if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + const auto net_arrival = nd.max_arrival; + auto path_budget = clk_period - (net_arrival + net_delay); + auto budget_share = path_budget / net_length_plus_one; + if (budget_override) + budget_share = 0; + else + usr.budget = std::min(usr.budget, net_delay + budget_share); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + + min_slack = std::min(min_slack, path_budget); + if (slack_histogram) { + int slack_ps = ctx->getDelayNS(path_budget) * 1000; + (*slack_histogram)[slack_ps]++; + } + } else { + // Follow outputs of the user + for (auto port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + DelayInfo comb_delay; + // Look up delay through this path + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto path_budget = net_data.at(port.second.net).min_remaining_budget; + auto budget_share = path_budget / net_length_plus_one; + if (budget_override) + budget_share = 0; + else + usr.budget = std::min(usr.budget, net_delay + budget_share); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + } + } + } + } + } + } +#endif return min_slack; } void assign_budget() { // Clear delays to a very high value first - delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); for (auto &net : ctx->nets) { for (auto &usr : net.second->users) { - usr.budget = default_slack; + usr.budget = std::numeric_limits::max(); } } -- cgit v1.2.3 From fffaaa613f498cb83f8d885e5a8b75dc41a9c157 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 6 Aug 2018 19:32:17 +0200 Subject: Added project loader --- common/command.cc | 15 +++++++++- common/command.h | 2 ++ common/project.cc | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ common/project.h | 42 ++++++++++++++++++++++++++ ecp5/arch.h | 1 + ecp5/project.cc | 57 +++++++++++++++++++++++++++++++++++ generic/arch.cc | 2 +- generic/arch.h | 2 ++ generic/project.cc | 41 +++++++++++++++++++++++++ ice40/arch.h | 1 + ice40/project.cc | 71 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 common/project.cc create mode 100644 common/project.h create mode 100644 ecp5/project.cc create mode 100644 generic/project.cc create mode 100644 ice40/project.cc diff --git a/common/command.cc b/common/command.cc index f144db37..9eace237 100644 --- a/common/command.cc +++ b/common/command.cc @@ -187,7 +187,9 @@ int CommandHandler::executeMain(std::unique_ptr ctx) log_error("Loading design failed.\n"); customAfterLoad(ctx.get()); + } + if (vm.count("json") || vm.count("load")) { if (!ctx->pack() && !ctx->force) log_error("Packing design failed.\n"); ctx->check(); @@ -215,6 +217,11 @@ int CommandHandler::executeMain(std::unique_ptr ctx) deinit_python(); } #endif + + if (vm.count("save")) { + project.save(ctx.get(), vm["save"].as()); + } + return 0; } @@ -235,7 +242,13 @@ int CommandHandler::exec() if (executeBeforeContext()) return 0; - std::unique_ptr ctx = createContext(); + + std::unique_ptr ctx; + if (vm.count("load")) { + ctx = project.load(vm["load"].as()); + } else { + ctx = createContext(); + } setupContext(ctx.get()); setupArchContext(ctx.get()); return executeMain(std::move(ctx)); diff --git a/common/command.h b/common/command.h index e6595f6a..13d5a250 100644 --- a/common/command.h +++ b/common/command.h @@ -23,6 +23,7 @@ #include #include "nextpnr.h" +#include "project.h" NEXTPNR_NAMESPACE_BEGIN @@ -61,6 +62,7 @@ class CommandHandler po::positional_options_description pos; int argc; char **argv; + ProjectHandler project; }; NEXTPNR_NAMESPACE_END diff --git a/common/project.cc b/common/project.cc new file mode 100644 index 00000000..8e043f36 --- /dev/null +++ b/common/project.cc @@ -0,0 +1,87 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 +#include +#include "project.h" +#include "log.h" +#include "jsonparse.h" +#include + +NEXTPNR_NAMESPACE_BEGIN + +void ProjectHandler::save(Context *ctx, std::string filename) +{ + std::ofstream f(filename); + pt::ptree root; + 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)); +/* root.put("project.input.json", );*/ + root.put("project.params.freq", int(ctx->target_freq / 1e6)); + root.put("project.params.seed", ctx->rngstate); + saveArch(ctx,root); + pt::write_json(f, root); +} + +std::unique_ptr ProjectHandler::load(std::string filename) +{ + std::unique_ptr 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("project.version"); + if (version != 1) + log_error("Wrong project format version.\n"); + + + ctx = createContext(root); + + std::string arch_name = root.get("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 filename = input.get("json"); + boost::filesystem::path json = proj.parent_path() / filename; + std::ifstream f(json.string()); + if (!parse_json_file(f, filename, 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("freq") * 1e6; + if (params.count("seed")) + ctx->rngseed(params.get("seed")); + } + loadArch(ctx.get(),root, proj.parent_path().string()); + } catch (...) { + log_error("Error loading project file.\n"); + } + return ctx; +} + +NEXTPNR_NAMESPACE_END diff --git a/common/project.h b/common/project.h new file mode 100644 index 00000000..14f03ecd --- /dev/null +++ b/common/project.h @@ -0,0 +1,42 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace pt = boost::property_tree; + +struct ProjectHandler +{ + void save(Context *ctx, std::string filename); + std::unique_ptr load(std::string filename); + // implemented per arch + void saveArch(Context *ctx, pt::ptree &root); + std::unique_ptr createContext(pt::ptree &root); + void loadArch(Context *ctx, pt::ptree &root, std::string path); +}; + +NEXTPNR_NAMESPACE_END + +#endif // PROJECT_H diff --git a/ecp5/arch.h b/ecp5/arch.h index 6daf543d..223f1ec5 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -415,6 +415,7 @@ struct Arch : BaseCtx std::string getChipName() const; IdString archId() const { return id("ecp5"); } + ArchArgs archArgs() const { return args; } IdString archArgsToId(ArchArgs args) const; IdString belTypeToId(BelType type) const; diff --git a/ecp5/project.cc b/ecp5/project.cc new file mode 100644 index 00000000..a9b48bbe --- /dev/null +++ b/ecp5/project.cc @@ -0,0 +1,57 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 +#include +#include "project.h" +#include "log.h" +#include + +NEXTPNR_NAMESPACE_BEGIN + +void ProjectHandler::saveArch(Context *ctx, pt::ptree &root) +{ + root.put("project.arch.package", ctx->archArgs().package); + root.put("project.arch.speed", ctx->archArgs().speed); +} + +std::unique_ptr ProjectHandler::createContext(pt::ptree &root) +{ + ArchArgs chipArgs; + std::string arch_type = root.get("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("project.arch.package"); + chipArgs.speed = root.get("project.arch.speed"); + + return std::unique_ptr(new Context(chipArgs)); +} + +void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) +{ +} + +NEXTPNR_NAMESPACE_END diff --git a/generic/arch.cc b/generic/arch.cc index a0ab58f8..3389ac0d 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -175,7 +175,7 @@ void Arch::setGroupDecal(GroupId group, DecalXY decalxy) // --------------------------------------------------------------- -Arch::Arch(ArchArgs) : chipName("generic") {} +Arch::Arch(ArchArgs args) : chipName("generic"), args(args) {} void IdString::initialize_arch(const BaseCtx *ctx) {} diff --git a/generic/arch.h b/generic/arch.h index d3e8f69e..10cd1d5a 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -118,11 +118,13 @@ struct Arch : BaseCtx // --------------------------------------------------------------- // Common Arch API. Every arch must provide the following methods. + ArchArgs args; Arch(ArchArgs args); std::string getChipName() const { return chipName; } IdString archId() const { return id("generic"); } + ArchArgs archArgs() const { return args; } IdString archArgsToId(ArchArgs args) const { return id("none"); } IdString belTypeToId(BelType type) const { return type; } diff --git a/generic/project.cc b/generic/project.cc new file mode 100644 index 00000000..e412d8dc --- /dev/null +++ b/generic/project.cc @@ -0,0 +1,41 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 +#include "project.h" +#include "log.h" +#include + +NEXTPNR_NAMESPACE_BEGIN + +void ProjectHandler::saveArch(Context *ctx, pt::ptree &root) +{ +} + +std::unique_ptr ProjectHandler::createContext(pt::ptree &root) +{ + ArchArgs chipArgs; + return std::unique_ptr(new Context(chipArgs)); +} + +void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) +{ +} + +NEXTPNR_NAMESPACE_END diff --git a/ice40/arch.h b/ice40/arch.h index d3076416..bc968b74 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -411,6 +411,7 @@ struct Arch : BaseCtx std::string getChipName() const; IdString archId() const { return id("ice40"); } + ArchArgs archArgs() const { return args; } IdString archArgsToId(ArchArgs args) const; IdString belTypeToId(BelType type) const; diff --git a/ice40/project.cc b/ice40/project.cc new file mode 100644 index 00000000..53401d63 --- /dev/null +++ b/ice40/project.cc @@ -0,0 +1,71 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 +#include "project.h" +#include "log.h" +#include +#include "pcf.h" + +NEXTPNR_NAMESPACE_BEGIN + +void ProjectHandler::saveArch(Context *ctx, pt::ptree &root) +{ + root.put("project.arch.package", ctx->archArgs().package); + // if(!pcfFilename.empty()) + // root.put("project.input.pcf", pcfFilename); +} + +std::unique_ptr ProjectHandler::createContext(pt::ptree &root) +{ + ArchArgs chipArgs; + std::string arch_type = root.get("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; + } + chipArgs.package = root.get("project.arch.package"); + + return std::unique_ptr(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("pcf"); + std::ifstream f(pcf.string()); + if (!apply_pcf(ctx, f)) + log_error("Loading PCF failed.\n"); +} + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From b0741e292c7ec7191f2c92fe7695e34018469b67 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 6 Aug 2018 19:47:01 +0200 Subject: Use ProjectHandler from gui as well --- gui/ice40/mainwindow.cc | 95 +++++-------------------------------------------- 1 file changed, 9 insertions(+), 86 deletions(-) diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index 40b863e9..9870cb0d 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -24,13 +24,13 @@ #include #include #include -#include -#include #include "bitstream.h" #include "design_utils.h" #include "jsonparse.h" #include "log.h" #include "pcf.h" +#include "project.h" +#include static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } @@ -168,79 +168,16 @@ void MainWindow::newContext(Context *ctx) void MainWindow::open_proj() { - QMap arch; -#ifdef ICE40_HX1K_ONLY - arch.insert("hx1k", ArchArgs::HX1K); -#else - arch.insert("lp384", ArchArgs::LP384); - arch.insert("lp1k", ArchArgs::LP1K); - arch.insert("hx1k", ArchArgs::HX1K); - arch.insert("up5k", ArchArgs::UP5K); - arch.insert("lp8k", ArchArgs::LP8K); - arch.insert("hx8k", ArchArgs::HX8K); -#endif - QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj")); if (!fileName.isEmpty()) { try { - namespace pt = boost::property_tree; - - std::string fn = fileName.toStdString(); - currentProj = fn; + ProjectHandler proj; disableActions(); - - pt::ptree root; - std::string filename = fileName.toStdString(); - pt::read_json(filename, root); - log_info("Loading project %s...\n", filename.c_str()); - log_break(); - - int version = root.get("project.version"); - if (version != 1) - log_error("Wrong project format version.\n"); - - std::string arch_name = root.get("project.arch.name"); - if (arch_name != "ice40") - log_error("Unsuported project architecture.\n"); - - std::string arch_type = root.get("project.arch.type"); - std::string arch_package = root.get("project.arch.package"); - - chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(arch_type); - chipArgs.package = arch_package; - ctx = std::unique_ptr(new Context(chipArgs)); + ctx = proj.load(fileName.toStdString()); Q_EMIT contextChanged(ctx.get()); - - QFileInfo fi(fileName); - QDir::setCurrent(fi.absoluteDir().absolutePath()); - log_info("Setting current dir to %s...\n", fi.absoluteDir().absolutePath().toStdString().c_str()); - log_info("Loading project %s...\n", filename.c_str()); - log_info("Context changed to %s (%s)\n", arch_type.c_str(), arch_package.c_str()); - - auto project = root.get_child("project"); - std::string json; - std::string pcf; - if (project.count("input")) { - auto input = project.get_child("input"); - if (input.count("json")) - json = input.get("json"); - if (input.count("pcf")) - pcf = input.get("pcf"); - } - - if (!(QFileInfo::exists(json.c_str()) && QFileInfo(json.c_str()).isFile())) { - log_error("Json file does not exist.\n"); - } - if (!pcf.empty()) { - if (!(QFileInfo::exists(pcf.c_str()) && QFileInfo(pcf.c_str()).isFile())) { - log_error("PCF file does not exist.\n"); - } - } - - log_info("Loading json: %s...\n", json.c_str()); - load_json(json); - if (!pcf.empty()) - load_pcf(json); + log_info("Loaded project %s...\n", fileName.toStdString().c_str()); + updateJsonLoaded(); + actionLoadPCF->setEnabled(false); } catch (log_execution_error_exception) { } } @@ -263,22 +200,8 @@ bool MainWindow::save_proj() currentProj = fileName.toStdString(); } if (!currentProj.empty()) { - namespace pt = boost::property_tree; - QFileInfo fi(currentProj.c_str()); - QDir dir(fi.absoluteDir().absolutePath()); - std::ofstream f(currentProj); - pt::ptree root; - root.put("project.version", 1); - root.put("project.name", fi.baseName().toStdString()); - root.put("project.arch.name", ctx->archId().c_str(ctx.get())); - root.put("project.arch.type", ctx->archArgsToId(chipArgs).c_str(ctx.get())); - root.put("project.arch.package", chipArgs.package); - if (!currentJson.empty()) - root.put("project.input.json", dir.relativeFilePath(currentJson.c_str()).toStdString()); - if (!currentPCF.empty()) - root.put("project.input.pcf", dir.relativeFilePath(currentPCF.c_str()).toStdString()); - pt::write_json(f, root); - log_info("Project %s saved...\n", fi.baseName().toStdString().c_str()); + ProjectHandler proj; + proj.save(ctx.get(), currentProj); return true; } return false; -- cgit v1.2.3 From 3f5c0373a5eb77cf4cd40f1d280452bfbe6f42f8 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 12:03:58 -0700 Subject: Consider clocked cells with COUT, consider constant nets --- common/timing.cc | 58 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 72093183..12a4940a 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -155,7 +155,7 @@ struct Timing for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - bool is_io = cell.second->type == ctx->id_sb_io; + bool is_io = cell.second->type == ctx->id_sb_io; // HACK HACK HACK for (auto& port : cell.second->ports) { if (!port.second.net) continue; if (port.second.type == PORT_OUT) @@ -187,28 +187,41 @@ struct Timing } } + auto it = ctx->nets.find(ctx->id("$PACKER_VCC_NET")); + if (it != ctx->nets.end()) { + topographical_order.emplace_back(it->second.get()); + net_data.emplace(it->second.get(), TimingData{}); + } + it = ctx->nets.find(ctx->id("$PACKER_GND_NET")); + if (it != ctx->nets.end()) { + topographical_order.emplace_back(it->second.get()); + net_data.emplace(it->second.get(), TimingData{}); + } + std::deque queue(topographical_order.begin(), topographical_order.end()); while (!queue.empty()) { const auto net = queue.front(); queue.pop_front(); + DelayInfo clkToQ; for (auto &usr : net->users) { - if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { - } else { - // Follow outputs of the user - for (auto& port : usr.cell->ports) { - if (port.second.type == PORT_OUT && port.second.net) { - DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - auto it = port_fanin.find(&port.second); - NPNR_ASSERT(it != port_fanin.end()); - if (--it->second == 0) { - topographical_order.emplace_back(port.second.net); - queue.emplace_back(port.second.net); - port_fanin.erase(it); - } + auto clock_domain = ctx->getPortClock(usr.cell, usr.port); + // Follow outputs of the user + for (auto& port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + // Skip if this is a clocked output + if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) + continue; + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto it = port_fanin.find(&port.second); + NPNR_ASSERT(it != port_fanin.end()); + if (--it->second == 0) { + topographical_order.emplace_back(port.second.net); + queue.emplace_back(port.second.net); + port_fanin.erase(it); } } } @@ -216,6 +229,9 @@ struct Timing } } + NPNR_ASSERT(port_fanin.empty()); + port_fanin.clear(); + // Find the maximum arrival time and max path length for each net for (auto net : topographical_order) { auto &nd = net_data.at(net); @@ -225,7 +241,7 @@ struct Timing for (auto &usr : net->users) { if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { } else { - auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); auto usr_arrival = net_arrival + net_delay; // Follow outputs of the user @@ -254,13 +270,14 @@ struct Timing const delay_t net_length_plus_one = nd.max_path_length + 1; auto& net_min_remaining_budget = nd.min_remaining_budget; for (auto &usr : net->users) { - const auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { const auto net_arrival = nd.max_arrival; auto path_budget = clk_period - (net_arrival + net_delay); auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - usr.budget = std::min(usr.budget, net_delay + budget_share); + if (update) + usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); min_slack = std::min(min_slack, path_budget); @@ -278,7 +295,8 @@ struct Timing if (is_path) { auto path_budget = net_data.at(port.second.net).min_remaining_budget; auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - usr.budget = std::min(usr.budget, net_delay + budget_share); + if (update) + usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); } } -- cgit v1.2.3 From 21cd1d7dd6c1d033c933006a96b5eebfef220a9d Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 12:11:47 -0700 Subject: Add new Arch::isIOCell() API function --- ice40/arch.cc | 5 +++++ ice40/arch.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/ice40/arch.cc b/ice40/arch.cc index 1e7a383b..b142ae8b 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -898,6 +898,11 @@ bool Arch::isGlobalNet(const NetInfo *net) const return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out; } +bool Arch::isIOCell(const CellInfo *cell) const +{ + return cell->type == id_sb_io; +} + // Assign arch arg info void Arch::assignArchInfo() { diff --git a/ice40/arch.h b/ice40/arch.h index 328950df..2b785079 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -794,6 +794,8 @@ struct Arch : BaseCtx bool isClockPort(const CellInfo *cell, IdString port) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; + // Return true if a cell is an IO + bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- -- cgit v1.2.3 From daedf732911bc6d149e7c6db14f4fd976c79bbac Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 12:12:03 -0700 Subject: Use new Arch::isIOCell() function in Timing --- common/timing.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/timing.cc b/common/timing.cc index 12a4940a..eb3cdc55 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -155,7 +155,7 @@ struct Timing for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - bool is_io = cell.second->type == ctx->id_sb_io; // HACK HACK HACK + bool is_io = ctx->isIOCell(cell.second.get()); for (auto& port : cell.second->ports) { if (!port.second.net) continue; if (port.second.type == PORT_OUT) -- cgit v1.2.3 From 519b755acb9155caef0346fd478a6bc02ef9ae8d Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 13:12:24 -0700 Subject: Add comments --- common/timing.cc | 54 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 11383cee..49e8a5ef 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -127,9 +127,9 @@ struct Timing { const auto clk_period = delay_t(1.0e12 / ctx->target_freq); +#if 0 // Go through all clocked drivers and distribute the available path // slack evenly into the budget of every sink on the path -#if 0 for (auto &cell : ctx->cells) { for (auto port : cell.second->ports) { if (port.second.type == PORT_OUT) { @@ -147,16 +147,20 @@ struct Timing } #else + // First, compute the topographical order of nets to walk through + // the circuit, assuming it is a _acyclic_ graph + // TODO: Handle the case where it is cyclic, e.g. combinatorial loops std::vector topographical_order; - std::unordered_map port_fanin; std::unordered_map net_data; + // In lieu of deleting edges from the graph, simply count + // the number of fanins to each output port + std::unordered_map port_fanin; std::vector input_ports; std::vector output_ports; for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - bool is_io = ctx->isIOCell(cell.second.get()); for (auto& port : cell.second->ports) { if (!port.second.net) continue; if (port.second.type == PORT_OUT) @@ -165,8 +169,11 @@ struct Timing input_ports.push_back(port.first); } + bool is_io = ctx->isIOCell(cell.second.get()); for (auto o : output_ports) { IdString clock_domain = ctx->getPortClock(cell.second.get(), o->name); + // If output port is influenced by a clock (e.g. FF output) + // then add it to the ordering as a timing start-point if (clock_domain != IdString()) { DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); @@ -174,10 +181,14 @@ struct Timing net_data.emplace(o->net, TimingData{ clkToQ.maxDelay() }); } else { + // Also add I/O cells too if (is_io) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } + // Otherwise, for all driven input ports on this cell, + // if a timing arch exists between the input and + // the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); @@ -188,6 +199,7 @@ struct Timing } } + // If these constant nets exist, add them to the topographical ordering too auto it = ctx->nets.find(ctx->id("$PACKER_VCC_NET")); if (it != ctx->nets.end()) { topographical_order.emplace_back(it->second.get()); @@ -201,6 +213,8 @@ struct Timing std::deque queue(topographical_order.begin(), topographical_order.end()); + // Now walk the design, from the start points identified previously, building + // up a topographical order while (!queue.empty()) { const auto net = queue.front(); queue.pop_front(); @@ -208,15 +222,16 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { auto clock_domain = ctx->getPortClock(usr.cell, usr.port); - // Follow outputs of the user for (auto& port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { - // Skip if this is a clocked output + // Skip if this is a clocked output (but allow non-clocked ones) if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) continue; DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (is_path) { + // Decrement the fanin count, and only add to topographical + // order if all its fanins have already been visited auto it = port_fanin.find(&port.second); NPNR_ASSERT(it != port_fanin.end()); if (--it->second == 0) { @@ -230,10 +245,25 @@ struct Timing } } +#if 0 + // Sanity check to ensure that all ports where fanins were recorded + // were indeed visited + log_info("port_fanin = %d\n", port_fanin.size()); + for (auto i : port_fanin) { + log_info("%s %s.%s has %d fanins left\n", i.first->net->name.c_str(ctx),i.first->net->driver.cell->name.c_str(ctx), i.first->name.c_str(ctx), i.second); + auto cell = i.first->net->driver.cell; + for (auto& port : cell->ports) { + if (!port.second.net) continue; + if (port.second.type == PORT_IN) + log_info(" %s connected to %s\n", port.second.name.c_str(ctx), port.second.net->name.c_str(ctx)); + } + } NPNR_ASSERT(port_fanin.empty()); +#endif port_fanin.clear(); - // Find the maximum arrival time and max path length for each net + // Go forwards topographically to find the maximum arrival time + // and max path length for each net for (auto net : topographical_order) { auto &nd = net_data.at(net); const auto net_arrival = nd.max_arrival; @@ -245,7 +275,7 @@ struct Timing auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); auto usr_arrival = net_arrival + net_delay; - // Follow outputs of the user + // Iterate over all output ports on the same cell as the sink for (auto port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { DelayInfo comb_delay; @@ -255,7 +285,8 @@ struct Timing auto& data = net_data[port.second.net]; auto& arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { + if (!budget_override) { // Do not increment path length if budget overriden + // since it doesn't require a share of the slack auto& path_length = data.max_path_length; path_length = std::max(path_length, net_length_plus_one); } @@ -266,6 +297,8 @@ struct Timing } } + // Now go backwards topographically to determine the minimum path slack, + // and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; @@ -287,11 +320,10 @@ struct Timing (*slack_histogram)[slack_ps]++; } } else { - // Follow outputs of the user - for (auto port : usr.cell->ports) { + // Iterate over all output ports on the same cell as the sink + for (const auto& port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { DelayInfo comb_delay; - // Look up delay through this path bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (is_path) { auto path_budget = net_data.at(port.second.net).min_remaining_budget; -- cgit v1.2.3 From 06584f2e74bff6bf5a179e7f4a0dbebda7ef6caf Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 14:14:41 -0700 Subject: Compute critical path report --- common/timing.cc | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 49e8a5ef..8fe5cd49 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -145,7 +145,6 @@ struct Timing } } } - #else // First, compute the topographical order of nets to walk through // the circuit, assuming it is a _acyclic_ graph @@ -297,6 +296,8 @@ struct Timing } } + const NetInfo* crit_net = nullptr; + // Now go backwards topographically to determine the minimum path slack, // and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { @@ -314,7 +315,14 @@ struct Timing usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); - min_slack = std::min(min_slack, path_budget); + if (path_budget < min_slack) { + min_slack = path_budget; + if (crit_path) { + crit_path->clear(); + crit_path->push_back(&usr); + crit_net = net; + } + } if (slack_histogram) { int slack_ps = ctx->getDelayNS(path_budget) * 1000; (*slack_histogram)[slack_ps]++; @@ -337,6 +345,45 @@ struct Timing } } } + + if (crit_path) { + // Walk backwards from the most critical net + while (crit_net) { + const PortInfo* crit_ipin = nullptr; + delay_t max_arrival = std::numeric_limits::min(); + + // Look at all input ports on its driving cell + for (const auto& port : crit_net->driver.cell->ports) { + if (port.second.type == PORT_IN && port.second.net) { + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); + if (is_path) { + // If input port is influenced by a clock, skip + if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) + continue; + + // And find the fanin net with the latest arrival time + const auto net_arrival = net_data.at(port.second.net).max_arrival; + if (net_arrival > max_arrival) { + max_arrival = net_arrival; + crit_ipin = &port.second; + } + } + } + } + + if (!crit_ipin) break; + + for (auto &usr : crit_ipin->net->users) { + if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) { + crit_path->push_back(&usr); + break; + } + } + crit_net = crit_ipin->net; + } + std::reverse(crit_path->begin(), crit_path->end()); + } #endif return min_slack; } -- cgit v1.2.3 From f3e46df7095582b23ffcd4726e1b33cd77df6ba0 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 16:09:17 -0700 Subject: Remove old timing code --- common/timing.cc | 88 -------------------------------------------------------- 1 file changed, 88 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 8fe5cd49..b1be8dad 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -37,7 +37,6 @@ struct Timing bool net_delays; bool update; delay_t min_slack; - PortRefVector current_path; PortRefVector *crit_path; DelayFrequency *slack_histogram; @@ -56,96 +55,10 @@ struct Timing { } - delay_t follow_net(NetInfo *net, int path_length, delay_t slack) - { - const delay_t default_budget = slack / (path_length + 1); - delay_t net_budget = default_budget; - for (auto &usr : net->users) { - auto delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); - if (crit_path) - current_path.push_back(&usr); - // If budget override exists, use that value and do not increment path_length - auto budget = default_budget; - if (ctx->getBudgetOverride(net, usr, budget)) { - if (update) - usr.budget = std::min(usr.budget, budget); - budget = follow_user_port(usr, path_length, slack - budget); - net_budget = std::min(net_budget, budget); - } - else { - budget = follow_user_port(usr, path_length + 1, slack - delay); - net_budget = std::min(net_budget, budget); - if (update) - usr.budget = std::min(usr.budget, delay + budget); - } - if (crit_path) - current_path.pop_back(); - } - return net_budget; - } - - // Follow a path, returning budget to annotate - delay_t follow_user_port(PortRef &user, int path_length, delay_t slack) - { - delay_t value; - if (ctx->getPortClock(user.cell, user.port) != IdString()) { - // At the end of a timing path (arguably, should check setup time - // here too) - value = slack / path_length; - if (slack < min_slack) { - min_slack = slack; - if (crit_path) - *crit_path = current_path; - } - if (slack_histogram) { - int slack_ps = ctx->getDelayNS(slack) * 1000; - (*slack_histogram)[slack_ps]++; - } - } else { - // Default to the path ending here, if no further paths found - value = slack / path_length; - // Follow outputs of the user - for (auto port : user.cell->ports) { - if (port.second.type == PORT_OUT) { - DelayInfo comb_delay; - // Look up delay through this path - bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay); - if (is_path) { - NetInfo *net = port.second.net; - if (net) { - delay_t path_budget = follow_net(net, path_length, slack - comb_delay.maxDelay()); - value = std::min(value, path_budget); - } - } - } - } - } - return value; - } - delay_t walk_paths() { const auto clk_period = delay_t(1.0e12 / ctx->target_freq); -#if 0 - // Go through all clocked drivers and distribute the available path - // slack evenly into the budget of every sink on the path - for (auto &cell : ctx->cells) { - for (auto port : cell.second->ports) { - if (port.second.type == PORT_OUT) { - IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first); - if (clock_domain != IdString()) { - delay_t slack = clk_period; // TODO: clock constraints - DelayInfo clkToQ; - if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ)) - slack -= clkToQ.maxDelay(); - if (port.second.net) - follow_net(port.second.net, 0, slack); - } - } - } - } -#else // First, compute the topographical order of nets to walk through // the circuit, assuming it is a _acyclic_ graph // TODO: Handle the case where it is cyclic, e.g. combinatorial loops @@ -384,7 +297,6 @@ struct Timing } std::reverse(crit_path->begin(), crit_path->end()); } -#endif return min_slack; } -- cgit v1.2.3 From 6768a5c03e59e75ac20c666419b4e42cc16118e1 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:17:39 -0700 Subject: Add Arch::isIOCell() to ecp5 and generic --- ecp5/arch.cc | 6 ++++++ ecp5/arch.h | 2 ++ generic/arch.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d2d62241..4a3e8ef3 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -500,6 +500,12 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } +bool Arch::isIOCell(const CellInfo *cell) const +{ + return cell->type == id("TRELLIS_IO"); +} + + std::vector> Arch::getTilesAtLocation(int row, int col) { std::vector> ret; diff --git a/ecp5/arch.h b/ecp5/arch.h index e00e111a..fd8d0a13 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -833,6 +833,8 @@ struct Arch : BaseCtx bool isClockPort(const CellInfo *cell, IdString port) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; + // Return true if a cell is an IO + bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- // Placement validity checks diff --git a/generic/arch.h b/generic/arch.h index 59fe8d05..e7010885 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -215,6 +215,8 @@ struct Arch : BaseCtx bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; IdString getPortClock(const CellInfo *cell, IdString port) const; bool isClockPort(const CellInfo *cell, IdString port) const; + // Return true if a cell is an IO + bool isIOCell(const CellInfo *cell) const; bool isValidBelForCell(CellInfo *cell, BelId bel) const; bool isBelLocationValid(BelId bel) const; -- cgit v1.2.3 From 483f8631068086d7f0bdf10f60205567e03d943e Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:20:29 -0700 Subject: Also add PLL outputs as timing startpoints --- common/timing.cc | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index b1be8dad..3a4f0f9c 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -61,7 +61,7 @@ struct Timing // First, compute the topographical order of nets to walk through // the circuit, assuming it is a _acyclic_ graph - // TODO: Handle the case where it is cyclic, e.g. combinatorial loops + // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops std::vector topographical_order; std::unordered_map net_data; // In lieu of deleting edges from the graph, simply count @@ -94,7 +94,8 @@ struct Timing } else { // Also add I/O cells too - if (is_io) { + // TODO(eddieh): More generic way of detecting PLLs + if (is_io || cell.second->type == ctx->id("ICESTORM_PLL")) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } @@ -157,22 +158,9 @@ struct Timing } } -#if 0 // Sanity check to ensure that all ports where fanins were recorded // were indeed visited - log_info("port_fanin = %d\n", port_fanin.size()); - for (auto i : port_fanin) { - log_info("%s %s.%s has %d fanins left\n", i.first->net->name.c_str(ctx),i.first->net->driver.cell->name.c_str(ctx), i.first->name.c_str(ctx), i.second); - auto cell = i.first->net->driver.cell; - for (auto& port : cell->ports) { - if (!port.second.net) continue; - if (port.second.type == PORT_IN) - log_info(" %s connected to %s\n", port.second.name.c_str(ctx), port.second.net->name.c_str(ctx)); - } - } NPNR_ASSERT(port_fanin.empty()); -#endif - port_fanin.clear(); // Go forwards topographically to find the maximum arrival time // and max path length for each net -- cgit v1.2.3 From f44a5fb904e6e52e8383e8f13d9b3f4ab9d7ce48 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:35:23 -0700 Subject: clangformat --- common/timing.cc | 65 +++++++++++++++++++++++++++++++------------------------- ecp5/arch.cc | 7 +----- ice40/arch.cc | 22 ++++++++++--------- ice40/arch.h | 14 ++++++------ 4 files changed, 57 insertions(+), 51 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 3a4f0f9c..e524cd19 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -20,11 +20,11 @@ #include "timing.h" #include +#include #include #include #include "log.h" #include "util.h" -#include NEXTPNR_NAMESPACE_BEGIN @@ -40,7 +40,8 @@ struct Timing PortRefVector *crit_path; DelayFrequency *slack_histogram; - struct TimingData { + struct TimingData + { TimingData() : max_arrival(), max_path_length(), min_remaining_budget() {} TimingData(delay_t max_arrival) : max_arrival(max_arrival), max_path_length(), min_remaining_budget() {} delay_t max_arrival; @@ -61,20 +62,22 @@ struct Timing // 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 - std::vector topographical_order; - std::unordered_map net_data; + // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial + // loops + std::vector topographical_order; + std::unordered_map net_data; // In lieu of deleting edges from the graph, simply count // the number of fanins to each output port - std::unordered_map port_fanin; + std::unordered_map port_fanin; std::vector input_ports; - std::vector output_ports; + std::vector output_ports; for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - for (auto& port : cell.second->ports) { - if (!port.second.net) continue; + for (auto &port : cell.second->ports) { + if (!port.second.net) + continue; if (port.second.type == PORT_OUT) output_ports.push_back(&port.second); else @@ -90,11 +93,10 @@ struct Timing DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); topographical_order.emplace_back(o->net); - net_data.emplace(o->net, TimingData{ clkToQ.maxDelay() }); - } - else { + net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); + } else { // Also add I/O cells too - // TODO(eddieh): More generic way of detecting PLLs + // TODO(eddieh): More generic way of detecting PLLs if (is_io || cell.second->type == ctx->id("ICESTORM_PLL")) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); @@ -124,9 +126,10 @@ struct Timing net_data.emplace(it->second.get(), TimingData{}); } - std::deque queue(topographical_order.begin(), topographical_order.end()); + std::deque queue(topographical_order.begin(), topographical_order.end()); - // Now walk the design, from the start points identified previously, building + // Now walk the design, from the start points identified previously, + // building // up a topographical order while (!queue.empty()) { const auto net = queue.front(); @@ -135,7 +138,7 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { auto clock_domain = ctx->getPortClock(usr.cell, usr.port); - for (auto& port : usr.cell->ports) { + for (auto &port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { // Skip if this is a clocked output (but allow non-clocked ones) if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) @@ -182,12 +185,13 @@ struct Timing // Look up delay through this path bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (is_path) { - auto& data = net_data[port.second.net]; - auto& arrival = data.max_arrival; + auto &data = net_data[port.second.net]; + auto &arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if budget overriden - // since it doesn't require a share of the slack - auto& path_length = data.max_path_length; + if (!budget_override) { // Do not increment path length if + // budget overriden + // since it doesn't require a share of the slack + auto &path_length = data.max_path_length; path_length = std::max(path_length, net_length_plus_one); } } @@ -197,14 +201,14 @@ struct Timing } } - const NetInfo* crit_net = nullptr; + const NetInfo *crit_net = nullptr; // Now go backwards topographically to determine the minimum path slack, // and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; - auto& net_min_remaining_budget = nd.min_remaining_budget; + auto &net_min_remaining_budget = nd.min_remaining_budget; for (auto &usr : net->users) { auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); @@ -230,7 +234,7 @@ struct Timing } } else { // Iterate over all output ports on the same cell as the sink - for (const auto& port : usr.cell->ports) { + for (const auto &port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); @@ -239,7 +243,8 @@ struct Timing auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; if (update) usr.budget = std::min(usr.budget, net_delay + budget_share); - net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + net_min_remaining_budget = + std::min(net_min_remaining_budget, path_budget - budget_share); } } } @@ -250,14 +255,15 @@ struct Timing if (crit_path) { // Walk backwards from the most critical net while (crit_net) { - const PortInfo* crit_ipin = nullptr; + const PortInfo *crit_ipin = nullptr; delay_t max_arrival = std::numeric_limits::min(); // Look at all input ports on its driving cell - for (const auto& port : crit_net->driver.cell->ports) { + for (const auto &port : crit_net->driver.cell->ports) { if (port.second.type == PORT_IN && port.second.net) { DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); + bool is_path = + ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); if (is_path) { // If input port is influenced by a clock, skip if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) @@ -273,7 +279,8 @@ struct Timing } } - if (!crit_ipin) break; + if (!crit_ipin) + break; for (auto &usr : crit_ipin->net->users) { if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) { diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 4a3e8ef3..de3abd44 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -375,7 +375,6 @@ BelId Arch::getPioByFunctionName(const std::string &name) const } std::vector Arch::getBelPins(BelId bel) const - { std::vector ret; NPNR_ASSERT(bel != BelId()); @@ -500,11 +499,7 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } -bool Arch::isIOCell(const CellInfo *cell) const -{ - return cell->type == id("TRELLIS_IO"); -} - +bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id("TRELLIS_IO"); } std::vector> Arch::getTilesAtLocation(int row, int col) { diff --git a/ice40/arch.cc b/ice40/arch.cc index 324934d0..16104033 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -291,7 +291,8 @@ BelId Arch::getBelByLocation(Loc loc) const BelRange Arch::getBelsByTile(int x, int y) const { - // In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates are used + // In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates + // are used BelRange br; br.b.cursor = Arch::getBelByLocation(Loc(x, y, 0)).index; @@ -645,23 +646,27 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay auto sink_loc = getBelLocation(sink.cell->bel); if (driver_loc.y == sink_loc.y) budget = 0; - else switch (args.type) { + else + switch (args.type) { #ifndef ICE40_HX1K_ONLY case ArchArgs::HX8K: #endif case ArchArgs::HX1K: - budget = 190; break; + budget = 190; + break; #ifndef ICE40_HX1K_ONLY case ArchArgs::LP384: case ArchArgs::LP1K: case ArchArgs::LP8K: - budget = 290; break; + budget = 290; + break; case ArchArgs::UP5K: - budget = 560; break; + budget = 560; + break; #endif default: log_error("Unsupported iCE40 chip type.\n"); - } + } return true; } return false; @@ -913,10 +918,7 @@ bool Arch::isGlobalNet(const NetInfo *net) const return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out; } -bool Arch::isIOCell(const CellInfo *cell) const -{ - return cell->type == id_sb_io; -} +bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id_sb_io; } // Assign arch arg info void Arch::assignArchInfo() diff --git a/ice40/arch.h b/ice40/arch.h index 2b785079..a5be7e33 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -400,10 +400,10 @@ struct Arch : BaseCtx mutable std::unordered_map bel_by_loc; std::vector bel_carry; - std::vector bel_to_cell; - std::vector wire_to_net; - std::vector pip_to_net; - std::vector switches_locked; + std::vector bel_to_cell; + std::vector wire_to_net; + std::vector pip_to_net; + std::vector switches_locked; ArchArgs args; Arch(ArchArgs args); @@ -799,7 +799,8 @@ struct Arch : BaseCtx // ------------------------------------------------- - // Perform placement validity checks, returning false on failure (all implemented in arch_place.cc) + // Perform placement validity checks, returning false on failure (all + // implemented in arch_place.cc) // Whether or not a given cell can be placed at a given Bel // This is not intended for Bel type checks, but finer-grained constraints @@ -813,7 +814,8 @@ struct Arch : BaseCtx bool logicCellsCompatible(const std::vector &cells) const; // ------------------------------------------------- - // Assign architecure-specific arguments to nets and cells, which must be called between packing or further + // Assign architecure-specific arguments to nets and cells, which must be + // called between packing or further // netlist modifications, and validity checks void assignArchInfo(); void assignCellInfo(CellInfo *cell); -- cgit v1.2.3 From 676500b83fade3c2bc422a20f503ee1987cdb00f Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:42:44 -0700 Subject: Do less work if update flag is false --- common/timing.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index e524cd19..bb3f680d 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -215,10 +215,11 @@ struct Timing if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { const auto net_arrival = nd.max_arrival; auto path_budget = clk_period - (net_arrival + net_delay); - auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - if (update) + if (update) { + auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; usr.budget = std::min(usr.budget, net_delay + budget_share); - net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + } if (path_budget < min_slack) { min_slack = path_budget; @@ -232,7 +233,7 @@ struct Timing int slack_ps = ctx->getDelayNS(path_budget) * 1000; (*slack_histogram)[slack_ps]++; } - } else { + } else if (update) { // Iterate over all output ports on the same cell as the sink for (const auto &port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { @@ -241,8 +242,7 @@ struct Timing if (is_path) { auto path_budget = net_data.at(port.second.net).min_remaining_budget; auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - if (update) - usr.budget = std::min(usr.budget, net_delay + budget_share); + usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); } -- cgit v1.2.3 From a1d626469f46d112c2dfea680beb30ebdebc65fe Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 19:53:42 -0700 Subject: Cleanup nesting --- common/timing.cc | 118 +++++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index bb3f680d..841f8e92 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -139,23 +139,23 @@ struct Timing for (auto &usr : net->users) { auto clock_domain = ctx->getPortClock(usr.cell, usr.port); for (auto &port : usr.cell->ports) { - if (port.second.type == PORT_OUT && port.second.net) { - // Skip if this is a clocked output (but allow non-clocked ones) - if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) - continue; - DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - // Decrement the fanin count, and only add to topographical - // order if all its fanins have already been visited - auto it = port_fanin.find(&port.second); - NPNR_ASSERT(it != port_fanin.end()); - if (--it->second == 0) { - topographical_order.emplace_back(port.second.net); - queue.emplace_back(port.second.net); - port_fanin.erase(it); - } - } + if (port.second.type != PORT_OUT || !port.second.net) + continue; + // Skip if this is a clocked output (but allow non-clocked ones) + if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) + continue; + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (!is_path) + continue; + // Decrement the fanin count, and only add to topographical + // order if all its fanins have already been visited + auto it = port_fanin.find(&port.second); + NPNR_ASSERT(it != port_fanin.end()); + if (--it->second == 0) { + topographical_order.emplace_back(port.second.net); + queue.emplace_back(port.second.net); + port_fanin.erase(it); } } } @@ -180,21 +180,21 @@ struct Timing auto usr_arrival = net_arrival + net_delay; // Iterate over all output ports on the same cell as the sink for (auto port : usr.cell->ports) { - if (port.second.type == PORT_OUT && port.second.net) { - DelayInfo comb_delay; - // Look up delay through this path - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - auto &data = net_data[port.second.net]; - auto &arrival = data.max_arrival; - arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if - // budget overriden - // since it doesn't require a share of the slack - auto &path_length = data.max_path_length; - path_length = std::max(path_length, net_length_plus_one); - } - } + if (port.second.type != PORT_OUT || !port.second.net) + continue; + DelayInfo comb_delay; + // Look up delay through this path + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (!is_path) + continue; + auto &data = net_data[port.second.net]; + auto &arrival = data.max_arrival; + arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); + if (!budget_override) { // Do not increment path length if + // budget overriden + // since it doesn't require a share of the slack + auto &path_length = data.max_path_length; + path_length = std::max(path_length, net_length_plus_one); } } } @@ -236,17 +236,16 @@ struct Timing } else if (update) { // Iterate over all output ports on the same cell as the sink for (const auto &port : usr.cell->ports) { - if (port.second.type == PORT_OUT && port.second.net) { - DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - auto path_budget = net_data.at(port.second.net).min_remaining_budget; - auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - usr.budget = std::min(usr.budget, net_delay + budget_share); - net_min_remaining_budget = - std::min(net_min_remaining_budget, path_budget - budget_share); - } - } + if (port.second.type != PORT_OUT || !port.second.net) + continue; + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (!is_path) + continue; + auto path_budget = net_data.at(port.second.net).min_remaining_budget; + auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; + usr.budget = std::min(usr.budget, net_delay + budget_share); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); } } } @@ -260,28 +259,29 @@ struct Timing // Look at all input ports on its driving cell for (const auto &port : crit_net->driver.cell->ports) { - if (port.second.type == PORT_IN && port.second.net) { - DelayInfo comb_delay; - bool is_path = - ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); - if (is_path) { - // If input port is influenced by a clock, skip - if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) - continue; - - // And find the fanin net with the latest arrival time - const auto net_arrival = net_data.at(port.second.net).max_arrival; - if (net_arrival > max_arrival) { - max_arrival = net_arrival; - crit_ipin = &port.second; - } - } + if (port.second.type != PORT_IN || !port.second.net) + continue; + DelayInfo comb_delay; + bool is_path = + ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); + if (!is_path) + continue; + // If input port is influenced by a clock, skip + if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) + continue; + + // And find the fanin net with the latest arrival time + const auto net_arrival = net_data.at(port.second.net).max_arrival; + if (net_arrival > max_arrival) { + max_arrival = net_arrival; + crit_ipin = &port.second; } } if (!crit_ipin) break; + // Now convert PortInfo* into a PortRef* for (auto &usr : crit_ipin->net->users) { if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) { crit_path->push_back(&usr); -- cgit v1.2.3 From a0994d515454a696c98602980b298ee61aa03f4e Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 10:44:42 +0200 Subject: common: Add TimingPortClass Signed-off-by: David Shah --- common/nextpnr.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 3d9a66ca..d4925a16 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -291,6 +291,19 @@ struct CellInfo : ArchCellInfo // parent.[xyz] := 0 when (constr_parent == nullptr) }; +enum TimingPortClass +{ + TMG_CLOCK_INPUT, // Clock input to a sequential cell + TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc) + TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too) + TMG_REGISTER_OUTPUT, // Output from a register + TMG_COMB_INPUT, // Combinational input, no paths end here + TMG_COMB_OUTPUT, // Combinational output, no paths start here + TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output + TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input + TMG_ASYNC, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis +}; + struct DeterministicRNG { uint64_t rngstate; @@ -437,7 +450,7 @@ struct BaseCtx const Context *getCtx() const { return reinterpret_cast(this); } - template const char *nameOf(const T *obj) + template const char *nameOf(const T *obj) { if (obj == nullptr) return ""; -- cgit v1.2.3 From 4a44b1c96118c4a74c75c5d0791ee2395cf09900 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 8 Aug 2018 10:51:49 +0200 Subject: sync with master --- common/command.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/common/command.cc b/common/command.cc index 5b30df58..9c2cc840 100644 --- a/common/command.cc +++ b/common/command.cc @@ -36,6 +36,7 @@ #include "jsonparse.h" #include "log.h" #include "version.h" +#include NEXTPNR_NAMESPACE_BEGIN -- cgit v1.2.3 From bf42e525cb7ab6ae071b16dfeca55194878be69c Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 14:37:59 +0200 Subject: Arch API: New specification for timing port classes Signed-off-by: David Shah --- common/nextpnr.h | 2 +- ecp5/arch.cc | 7 +++--- ecp5/arch.h | 6 ++--- generic/arch.cc | 8 ++++--- generic/arch.h | 4 ++-- ice40/arch.cc | 68 +++++++++++++++++++++++++++++++++++++++++--------------- ice40/arch.h | 6 ++--- 7 files changed, 66 insertions(+), 35 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index d4925a16..938f4f95 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -301,7 +301,7 @@ enum TimingPortClass TMG_COMB_OUTPUT, // Combinational output, no paths start here TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input - TMG_ASYNC, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis + TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis }; struct DeterministicRNG diff --git a/ecp5/arch.cc b/ecp5/arch.cc index de3abd44..12707a03 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -495,9 +495,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return false; } -IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return IdString(); } - -bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const +{ + return TMG_IGNORE; +} bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id("TRELLIS_IO"); } diff --git a/ecp5/arch.h b/ecp5/arch.h index fd8d0a13..7bbb9da5 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -827,10 +827,8 @@ struct Arch : BaseCtx // Get the delay through a cell from one port to another, returning false // if no path exists bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; - // Get the associated clock to a port, or empty if the port is combinational - IdString getPortClock(const CellInfo *cell, IdString port) const; - // Return true if a port is a clock - bool isClockPort(const CellInfo *cell, IdString port) const; + // Get the port class, also setting clockPort if applicable + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; // Return true if a cell is an IO diff --git a/generic/arch.cc b/generic/arch.cc index 0fa93da8..25e4d08c 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -435,9 +435,11 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return false; } -IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return IdString(); } - -bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } +// Get the port class, also setting clockPort if applicable +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const +{ + return TMG_IGNORE; +} bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } bool Arch::isBelLocationValid(BelId bel) const { return true; } diff --git a/generic/arch.h b/generic/arch.h index e7010885..fb4f3660 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -213,8 +213,8 @@ struct Arch : BaseCtx DecalXY getGroupDecal(GroupId group) const; bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; - IdString getPortClock(const CellInfo *cell, IdString port) const; - bool isClockPort(const CellInfo *cell, IdString port) const; + // Get the port class, also setting clockPort if applicable + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; // Return true if a cell is an IO bool isIOCell(const CellInfo *cell) const; diff --git a/ice40/arch.cc b/ice40/arch.cc index 16104033..0f81bfea 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -27,6 +27,7 @@ #include "placer1.h" #include "router1.h" #include "util.h" + NEXTPNR_NAMESPACE_BEGIN // ----------------------------------------------------------------------- @@ -106,7 +107,9 @@ BelType Arch::belTypeFromId(IdString type) const void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, PIN_##t); + #include "portpins.inc" + #undef X } @@ -888,27 +891,56 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return false; } -IdString Arch::getPortClock(const CellInfo *cell, IdString port) const +// Get the port class, also setting clockPort to associated clock if applicable +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const { - if (cell->type == id_icestorm_lc && cell->lcInfo.dffEnable) { - if (port != id_lo && port != id_cin && port != id_cout) - return id_clk; - } else if (cell->type == id_icestorm_ram) { - if (port.str(this)[0] == 'R') - return id_rclk; + if (cell->type == id_icestorm_lc) { + if (port == id_clk) + return TMG_CLOCK_INPUT; + if (port == id_cin) + return TMG_COMB_INPUT; + if (port == id_cout || port == id_lo) + return TMG_COMB_OUTPUT; + if (cell->lcInfo.dffEnable) { + clockPort = id_clk; + if (port == id_o) + return TMG_REGISTER_OUTPUT; + else + return TMG_REGISTER_INPUT; + } else { + if (port == id_o) + return TMG_COMB_OUTPUT; + else + return TMG_COMB_INPUT; + } + } else if (cell->type == id_icestorm_ram || cell->type == id("ICESTORM_DSP") || + cell->type == id("ICESTORM_SPRAM")) { + if (port == id_clk) + return TMG_CLOCK_INPUT; + else if (cell->ports.at(port).type == PORT_OUT) + return TMG_REGISTER_OUTPUT; else - return id_wclk; + return TMG_REGISTER_INPUT; + } else if (cell->type == id_sb_io) { + if (port == id("D_IN_0") || port == id("D_IN_1")) + return TMG_STARTPOINT; + if (port == id("D_OUT_0") || port == id("D_OUT_1")) + return TMG_STARTPOINT; + return TMG_IGNORE; + } else if (cell->type == id("ICESTORM_PLL")) { + if (port == id("PLLOUT_A") || port == id("PLLOUT_B")) + return TMG_GEN_CLOCK; + return TMG_IGNORE; + } else if (cell->type == id("ICESTORM_LFOSC")) { + if (port == id("CLKLF")) + return TMG_GEN_CLOCK; + return TMG_IGNORE; + } else if (cell->type == id("ICESTORM_HFOSC")) { + if (port == id("CLKHF")) + return TMG_GEN_CLOCK; + return TMG_IGNORE; } - return IdString(); -} - -bool Arch::isClockPort(const CellInfo *cell, IdString port) const -{ - if (cell->type == id("ICESTORM_LC") && port == id("CLK")) - return true; - if (cell->type == id("ICESTORM_RAM") && (port == id("RCLK") || (port == id("WCLK")))) - return true; - return false; + return TMG_IGNORE; } bool Arch::isGlobalNet(const NetInfo *net) const diff --git a/ice40/arch.h b/ice40/arch.h index a5be7e33..7cc8495d 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -788,10 +788,8 @@ struct Arch : BaseCtx // Get the delay through a cell from one port to another, returning false // if no path exists bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; - // Get the associated clock to a port, or empty if the port is combinational - IdString getPortClock(const CellInfo *cell, IdString port) const; - // Return true if a port is a clock - bool isClockPort(const CellInfo *cell, IdString port) const; + // Get the port class, also setting clockDomain if applicable + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; // Return true if a cell is an IO -- cgit v1.2.3 From d8b383003194f45cae8486905e50575d792a4640 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 14:58:43 +0200 Subject: timing: Update to new use API (currently broken) Signed-off-by: David Shah --- common/timing.cc | 32 +++++++++++++++++++++----------- gui/ecp5/mainwindow.cc | 12 +++++++----- gui/ecp5/mainwindow.h | 1 + gui/generic/mainwindow.cc | 3 ++- ice40/arch.cc | 4 ++-- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 841f8e92..15a4d126 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -84,20 +84,20 @@ struct Timing input_ports.push_back(port.first); } - bool is_io = ctx->isIOCell(cell.second.get()); for (auto o : output_ports) { - IdString clock_domain = ctx->getPortClock(cell.second.get(), o->name); + IdString clockPort; + TimingPortClass portClass = ctx->getPortTimingClass(cell.second.get(), o->name, clockPort); // If output port is influenced by a clock (e.g. FF output) // then add it to the ordering as a timing start-point - if (clock_domain != IdString()) { + if (portClass == TMG_REGISTER_OUTPUT) { DelayInfo clkToQ; - ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); + ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ); topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { // Also add I/O cells too // TODO(eddieh): More generic way of detecting PLLs - if (is_io || cell.second->type == ctx->id("ICESTORM_PLL")) { + if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { // IGNORE: ???? topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } @@ -137,12 +137,13 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { - auto clock_domain = ctx->getPortClock(usr.cell, usr.port); + IdString clockPort; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); for (auto &port : usr.cell->ports) { if (port.second.type != PORT_OUT || !port.second.net) continue; // Skip if this is a clocked output (but allow non-clocked ones) - if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) + if (portClass == TMG_REGISTER_OUTPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) continue; DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); @@ -173,7 +174,9 @@ struct Timing const auto net_length_plus_one = nd.max_path_length + 1; nd.min_remaining_budget = clk_period; for (auto &usr : net->users) { - if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + IdString clockPort; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); + if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) { } else { auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); @@ -212,7 +215,9 @@ struct Timing for (auto &usr : net->users) { auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); - if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + IdString associatedClock; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, associatedClock); + if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT) { const auto net_arrival = nd.max_arrival; auto path_budget = clk_period - (net_arrival + net_delay); if (update) { @@ -267,7 +272,10 @@ struct Timing if (!is_path) continue; // If input port is influenced by a clock, skip - if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) + IdString portClock; + TimingPortClass portClass = ctx->getPortTimingClass(crit_net->driver.cell, port.first, portClock); + if (portClass == TMG_REGISTER_INPUT || portClass == TMG_CLOCK_INPUT || portClass == TMG_ENDPOINT || + portClass == TMG_IGNORE) continue; // And find the fanin net with the latest arrival time @@ -373,7 +381,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) auto &front = crit_path.front(); auto &front_port = front->cell->ports.at(front->port); auto &front_driver = front_port.net->driver; - auto last_port = ctx->getPortClock(front_driver.cell, front_driver.port); + + IdString last_port; + ctx->getPortTimingClass(front_driver.cell, front_driver.port, last_port); for (auto sink : crit_path) { auto sink_cell = sink->cell; auto &port = sink_cell->ports.at(sink->port); diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc index efaad364..e5a5f1ba 100644 --- a/gui/ecp5/mainwindow.cc +++ b/gui/ecp5/mainwindow.cc @@ -29,7 +29,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN -MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) : BaseMainWindow(std::move(context), args, parent) +MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) + : BaseMainWindow(std::move(context), args, parent) { initMainResource(); @@ -50,7 +51,8 @@ void MainWindow::newContext(Context *ctx) setWindowTitle(title.c_str()); } -void MainWindow::createMenu() { +void MainWindow::createMenu() +{ // Add arch specific actions actionLoadBase = new QAction("Open Base Config", this); actionLoadBase->setIcon(QIcon(":/icons/resources/open_base.png")); @@ -71,7 +73,7 @@ void MainWindow::createMenu() { menuDesign->addSeparator(); menuDesign->addAction(actionLoadBase); - menuDesign->addAction(actionSaveConfig); + menuDesign->addAction(actionSaveConfig); } static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return ptr->get(); } @@ -96,8 +98,8 @@ static QStringList getSupportedPackages(ArchArgs::ArchArgsTypes chip) return packages; } - -void MainWindow::new_proj() { +void MainWindow::new_proj() +{ QMap arch; arch.insert("Lattice ECP5 25K", ArchArgs::LFE5U_25F); arch.insert("Lattice ECP5 45K", ArchArgs::LFE5U_45F); diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h index d1d5a5a2..9460913c 100644 --- a/gui/ecp5/mainwindow.h +++ b/gui/ecp5/mainwindow.h @@ -47,6 +47,7 @@ class MainWindow : public BaseMainWindow void newContext(Context *ctx); void open_base(); void save_config(); + private: QAction *actionLoadBase; QAction *actionSaveConfig; diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc index 050c0fb8..01eeb4ef 100644 --- a/gui/generic/mainwindow.cc +++ b/gui/generic/mainwindow.cc @@ -23,7 +23,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN -MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) : BaseMainWindow(std::move(context), args, parent) +MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) + : BaseMainWindow(std::move(context), args, parent) { initMainResource(); diff --git a/ice40/arch.cc b/ice40/arch.cc index 0f81bfea..fddfabfd 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -915,7 +915,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id } } else if (cell->type == id_icestorm_ram || cell->type == id("ICESTORM_DSP") || cell->type == id("ICESTORM_SPRAM")) { - if (port == id_clk) + if (port == id_clk || port == id_rclk || port == id_wclk) return TMG_CLOCK_INPUT; else if (cell->ports.at(port).type == PORT_OUT) return TMG_REGISTER_OUTPUT; @@ -925,7 +925,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id if (port == id("D_IN_0") || port == id("D_IN_1")) return TMG_STARTPOINT; if (port == id("D_OUT_0") || port == id("D_OUT_1")) - return TMG_STARTPOINT; + return TMG_ENDPOINT; return TMG_IGNORE; } else if (cell->type == id("ICESTORM_PLL")) { if (port == id("PLLOUT_A") || port == id("PLLOUT_B")) -- cgit v1.2.3 From 787fe5661cedf42a55f6aeb285d164e6cdf56404 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 15:00:39 +0200 Subject: ice40: Timing arch fix Signed-off-by: David Shah --- ice40/arch.cc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index fddfabfd..bfab91a1 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -913,9 +913,23 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id else return TMG_COMB_INPUT; } - } else if (cell->type == id_icestorm_ram || cell->type == id("ICESTORM_DSP") || - cell->type == id("ICESTORM_SPRAM")) { - if (port == id_clk || port == id_rclk || port == id_wclk) + } else if (cell->type == id_icestorm_ram) { + + if (port == id_rclk || port == id_wclk) + return TMG_CLOCK_INPUT; + + if (port.str(this)[0] == 'R') + clockPort = id_rclk; + else + clockPort = id_wclk; + + if (cell->ports.at(port).type == PORT_OUT) + return TMG_REGISTER_OUTPUT; + else + return TMG_REGISTER_INPUT; + } else if (cell->type == id("ICESTORM_DSP") || cell->type == id("ICESTORM_SPRAM")) { + clockPort = id_clk; + if (port == id_clk) return TMG_CLOCK_INPUT; else if (cell->ports.at(port).type == PORT_OUT) return TMG_REGISTER_OUTPUT; -- cgit v1.2.3 From d173ddba367f7082f122df75dffb59577afaf0b4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 15:07:41 +0200 Subject: timing: Debugging implementation of new timing API Signed-off-by: David Shah --- common/timing.cc | 13 +++++++++---- ice40/arch.cc | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 15a4d126..0028fd21 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -96,8 +96,8 @@ struct Timing net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { // Also add I/O cells too - // TODO(eddieh): More generic way of detecting PLLs - if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { // IGNORE: ???? + // TODO: how to process ignore here + if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } @@ -138,12 +138,17 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { IdString clockPort; - TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); + TimingPortClass usrClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); + if (usrClass == TMG_IGNORE || usrClass == TMG_CLOCK_INPUT) + continue; for (auto &port : usr.cell->ports) { if (port.second.type != PORT_OUT || !port.second.net) continue; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, port.first, clockPort); + // Skip if this is a clocked output (but allow non-clocked ones) - if (portClass == TMG_REGISTER_OUTPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) + if (portClass == TMG_REGISTER_OUTPUT || portClass == TMG_STARTPOINT || portClass == TMG_IGNORE || + portClass == TMG_GEN_CLOCK) continue; DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); diff --git a/ice40/arch.cc b/ice40/arch.cc index bfab91a1..abce0aba 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -938,7 +938,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id } else if (cell->type == id_sb_io) { if (port == id("D_IN_0") || port == id("D_IN_1")) return TMG_STARTPOINT; - if (port == id("D_OUT_0") || port == id("D_OUT_1")) + if (port == id("D_OUT_0") || port == id("D_OUT_1") || port == id("OUTPUT_ENABLE")) return TMG_ENDPOINT; return TMG_IGNORE; } else if (cell->type == id("ICESTORM_PLL")) { -- cgit v1.2.3 From e6eb2038683fd3c81570b8c8e5307678bca5f77e Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 16:34:41 +0200 Subject: ice40: Add timing arcs through global buffers Signed-off-by: David Shah --- ice40/arch.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ice40/arch.cc b/ice40/arch.cc index abce0aba..3d31f980 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -953,6 +953,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id if (port == id("CLKHF")) return TMG_GEN_CLOCK; return TMG_IGNORE; + } else if (cell->type == id_sb_gb) { + if (port == id_glb_buf_out) + return TMG_COMB_OUTPUT; + return TMG_COMB_INPUT; } return TMG_IGNORE; } -- cgit v1.2.3 From fca01f5447d8ca0ba2e0075cb214f3a19933e6c1 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 07:49:07 -0700 Subject: Also include TMG_GEN_CLOCK as a timing startpoint --- common/timing.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 0028fd21..fae9ca53 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -95,14 +95,13 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { - // Also add I/O cells too // TODO: how to process ignore here - if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { + if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } // Otherwise, for all driven input ports on this cell, - // if a timing arch exists between the input and + // if a timing arc exists between the input and // the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; -- cgit v1.2.3 From d21e5a4b10c2eb57ae13e4622aa436339a96bcb9 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 07:58:01 -0700 Subject: Disable assign_budget() after placement legalisation, unless slack redist --- common/placer1.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/placer1.cc b/common/placer1.cc index 1d00e77a..51ef271a 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -236,7 +236,10 @@ class SAPlacer temp = post_legalise_temp; diameter *= post_legalise_dia_scale; ctx->shuffle(autoplaced); - assign_budget(ctx); + + // Legalisation is a big change so force a slack redistribution here + if (ctx->slack_redist_iter > 0) + assign_budget(ctx, true /* quiet */); } else if (ctx->slack_redist_iter > 0 && iter % ctx->slack_redist_iter == 0) { assign_budget(ctx, true /* quiet */); } -- cgit v1.2.3 From 936b52eafc6da47fdd9c5e56d2e5ae667f000944 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 08:01:24 -0700 Subject: Unfurl comments for clangformat --- common/timing.cc | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index fae9ca53..47091c27 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -60,14 +60,11 @@ struct Timing { const auto clk_period = delay_t(1.0e12 / ctx->target_freq); - // 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 + // 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 std::vector topographical_order; std::unordered_map net_data; - // In lieu of deleting edges from the graph, simply count - // the number of fanins to each output port + // In lieu of deleting edges from the graph, simply count the number of fanins to each output port std::unordered_map port_fanin; std::vector input_ports; @@ -87,8 +84,7 @@ struct Timing for (auto o : output_ports) { IdString clockPort; TimingPortClass portClass = ctx->getPortTimingClass(cell.second.get(), o->name, clockPort); - // If output port is influenced by a clock (e.g. FF output) - // then add it to the ordering as a timing start-point + // If output port is influenced by a clock (e.g. FF output) then add it to the ordering as a timing start-point if (portClass == TMG_REGISTER_OUTPUT) { DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ); @@ -100,9 +96,7 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } - // Otherwise, for all driven input ports on this cell, - // if a timing arc exists between the input and - // the current output port, increment fanin counter + // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); @@ -127,9 +121,7 @@ struct Timing std::deque queue(topographical_order.begin(), topographical_order.end()); - // Now walk the design, from the start points identified previously, - // building - // up a topographical order + // Now walk the design, from the start points identified previously, building up a topographical order while (!queue.empty()) { const auto net = queue.front(); queue.pop_front(); @@ -153,8 +145,7 @@ struct Timing bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (!is_path) continue; - // Decrement the fanin count, and only add to topographical - // order if all its fanins have already been visited + // Decrement the fanin count, and only add to topographical order if all its fanins have already been visited auto it = port_fanin.find(&port.second); NPNR_ASSERT(it != port_fanin.end()); if (--it->second == 0) { @@ -166,12 +157,10 @@ struct Timing } } - // Sanity check to ensure that all ports where fanins were recorded - // were indeed visited + // Sanity check to ensure that all ports where fanins were recorded were indeed visited NPNR_ASSERT(port_fanin.empty()); - // Go forwards topographically to find the maximum arrival time - // and max path length for each net + // Go forwards topographically to find the maximum arrival time and max path length for each net for (auto net : topographical_order) { auto &nd = net_data.at(net); const auto net_arrival = nd.max_arrival; @@ -197,9 +186,7 @@ struct Timing auto &data = net_data[port.second.net]; auto &arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if - // budget overriden - // since it doesn't require a share of the slack + if (!budget_override) { // Do not increment path length if budget overriden since it doesn't require a share of the slack auto &path_length = data.max_path_length; path_length = std::max(path_length, net_length_plus_one); } @@ -210,8 +197,7 @@ struct Timing const NetInfo *crit_net = nullptr; - // Now go backwards topographically to determine the minimum path slack, - // and to distribute all path slack evenly between all nets on the path + // Now go backwards topographically to determine the minimum path slack, and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; @@ -348,9 +334,7 @@ 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 + // 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.0e12 / ctx->target_freq); ctx->target_freq = 1e12 / (default_slack - timing.min_slack); -- cgit v1.2.3 From 433ad6462e401e722fbdd033c2b4b1c9a3537947 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:06:59 +0200 Subject: Arch API: Removing Arch::isIOCell Signed-off-by: David Shah --- ecp5/arch.cc | 2 -- ecp5/arch.h | 2 -- generic/arch.h | 2 -- ice40/arch.cc | 2 -- ice40/arch.h | 2 -- 5 files changed, 10 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 12707a03..9dac70d9 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -500,8 +500,6 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id return TMG_IGNORE; } -bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id("TRELLIS_IO"); } - std::vector> Arch::getTilesAtLocation(int row, int col) { std::vector> ret; diff --git a/ecp5/arch.h b/ecp5/arch.h index 7bbb9da5..17f8c58f 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -831,8 +831,6 @@ struct Arch : BaseCtx TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; - // Return true if a cell is an IO - bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- // Placement validity checks diff --git a/generic/arch.h b/generic/arch.h index fb4f3660..46801372 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -215,8 +215,6 @@ struct Arch : BaseCtx bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; // Get the port class, also setting clockPort if applicable TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; - // Return true if a cell is an IO - bool isIOCell(const CellInfo *cell) const; bool isValidBelForCell(CellInfo *cell, BelId bel) const; bool isBelLocationValid(BelId bel) const; diff --git a/ice40/arch.cc b/ice40/arch.cc index 3d31f980..888a0dee 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -968,8 +968,6 @@ bool Arch::isGlobalNet(const NetInfo *net) const return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out; } -bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id_sb_io; } - // Assign arch arg info void Arch::assignArchInfo() { diff --git a/ice40/arch.h b/ice40/arch.h index 7cc8495d..cdac1e96 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -792,8 +792,6 @@ struct Arch : BaseCtx TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; - // Return true if a cell is an IO - bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- -- cgit v1.2.3 From 90e3db324eb016b6c5ae4168db6ac00f65c2af4e Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:07:20 +0200 Subject: clangformat Signed-off-by: David Shah --- common/timing.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 47091c27..a3b5235a 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -84,7 +84,8 @@ struct Timing for (auto o : output_ports) { IdString clockPort; TimingPortClass portClass = ctx->getPortTimingClass(cell.second.get(), o->name, clockPort); - // If output port is influenced by a clock (e.g. FF output) then add it to the ordering as a timing start-point + // If output port is influenced by a clock (e.g. FF output) then add it to the ordering as a timing + // start-point if (portClass == TMG_REGISTER_OUTPUT) { DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ); @@ -96,7 +97,8 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } - // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and the current output port, increment fanin counter + // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and + // the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); @@ -145,7 +147,8 @@ struct Timing bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (!is_path) continue; - // Decrement the fanin count, and only add to topographical order if all its fanins have already been visited + // Decrement the fanin count, and only add to topographical order if all its fanins have already + // been visited auto it = port_fanin.find(&port.second); NPNR_ASSERT(it != port_fanin.end()); if (--it->second == 0) { @@ -186,7 +189,8 @@ struct Timing auto &data = net_data[port.second.net]; auto &arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if budget overriden since it doesn't require a share of the slack + if (!budget_override) { // Do not increment path length if budget overriden since it doesn't + // require a share of the slack auto &path_length = data.max_path_length; path_length = std::max(path_length, net_length_plus_one); } @@ -197,7 +201,8 @@ struct Timing const NetInfo *crit_net = nullptr; - // Now go backwards topographically to determine the minimum path slack, and to distribute all path slack evenly between all nets on the path + // Now go backwards topographically to determine the minimum path slack, and to distribute all path slack evenly + // between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; @@ -334,7 +339,8 @@ 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 + // 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.0e12 / ctx->target_freq); ctx->target_freq = 1e12 / (default_slack - timing.min_slack); -- cgit v1.2.3 From 91023d2a0e6c4d15b640f9c913565b8298f0a19c Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 08:31:08 -0700 Subject: Leave comment behind about removing false paths --- common/timing.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/timing.cc b/common/timing.cc index a3b5235a..c1be083d 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -92,7 +92,7 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { - // TODO: how to process ignore here + // TODO(eddieh): Generated clocks and ignored ports are currently added into the ordering as if it was a regular timing start point in order to enable the full topographical order to be computed, however these false nets (and their downstream paths) should not be in the final ordering if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); -- cgit v1.2.3 From 03575a2a7a07dd4798bf7acde6bd49258c2ec236 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 08:32:17 -0700 Subject: One more breadcrumb --- common/timing.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/common/timing.cc b/common/timing.cc index c1be083d..3c430739 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -110,6 +110,7 @@ struct Timing } // If these constant nets exist, add them to the topographical ordering too + // TODO(eddieh): Also false paths and should be removed from ordering auto it = ctx->nets.find(ctx->id("$PACKER_VCC_NET")); if (it != ctx->nets.end()) { topographical_order.emplace_back(it->second.get()); -- cgit v1.2.3 From 8e593fb47147ca7b4cd73d3cafc789d520871cb4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:54:25 +0200 Subject: timing: Update to use getDelayNS Signed-off-by: David Shah --- common/timing.cc | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 3c430739..601311a8 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -92,7 +92,9 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { - // TODO(eddieh): Generated clocks and ignored ports are currently added into the ordering as if it was a regular timing start point in order to enable the full topographical order to be computed, however these false nets (and their downstream paths) should not be in the final ordering + // TODO(eddieh): Generated clocks and ignored ports are currently added into the ordering as if it + // was a regular timing start point in order to enable the full topographical order to be computed, + // however these false nets (and their downstream paths) should not be in the final ordering if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); @@ -343,12 +345,12 @@ 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.0e12 / ctx->target_freq); - ctx->target_freq = 1e12 / (default_slack - timing.min_slack); + 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->verbose) - log_info("minimum slack for this assign = %d, target Fmax for next " + log_info("minimum slack for this assign = %.2f ns, target Fmax for next " "update = %.2f MHz\n", - timing.min_slack, ctx->target_freq / 1e6); + ctx->getDelayNS(timing.min_slack), ctx->target_freq / 1e6); } if (!quiet) @@ -388,14 +390,15 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) DelayInfo comb_delay; ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay); total += comb_delay.maxDelay(); - log_info("%4d %4d Source %s.%s\n", comb_delay.maxDelay(), total, driver_cell->name.c_str(ctx), - driver.port.c_str(ctx)); + log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(comb_delay.maxDelay()), ctx->getDelayNS(total), + driver_cell->name.c_str(ctx), driver.port.c_str(ctx)); auto net_delay = ctx->getNetinfoRouteDelay(net, *sink); total += net_delay; auto driver_loc = ctx->getBelLocation(driver_cell->bel); auto sink_loc = ctx->getBelLocation(sink_cell->bel); - log_info("%4d %4d Net %s budget %d (%d,%d) -> (%d,%d)\n", net_delay, total, net->name.c_str(ctx), - sink->budget, driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y); + log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay), + ctx->getDelayNS(total), net->name.c_str(ctx), ctx->getDelayNS(sink->budget), driver_loc.x, + driver_loc.y, sink_loc.x, sink_loc.y); log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx)); last_port = sink->port; } @@ -403,8 +406,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) } } - delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); - log_info("estimated Fmax = %.2f MHz\n", 1e6 / (default_slack - min_slack)); + delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->target_freq); + log_info("estimated Fmax = %.2f MHz\n", 1e3 / ctx->getDelayNS(default_slack - min_slack)); if (print_histogram && slack_histogram.size() > 0) { constexpr unsigned num_bins = 20; -- cgit v1.2.3 From 3e11ba8afb332909ee40760a222b2d2b56bc3232 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:55:54 +0200 Subject: timing: Remove unused variable Signed-off-by: David Shah --- common/timing.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/common/timing.cc b/common/timing.cc index 601311a8..aadd8381 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -131,7 +131,6 @@ struct Timing const auto net = queue.front(); queue.pop_front(); - DelayInfo clkToQ; for (auto &usr : net->users) { IdString clockPort; TimingPortClass usrClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); -- cgit v1.2.3 From 674cabb6bea05032c8bd7e638684ac7f6e448a6b Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:58:25 +0200 Subject: docs: Update Arch API Cell Timing docs Signed-off-by: David Shah --- docs/archapi.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/archapi.md b/docs/archapi.md index 222b9f78..3a2b5efb 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -455,13 +455,11 @@ Cell Delay Methods Returns the delay for the specified path through a cell in the `&delay` argument. The method returns false if there is no timing relationship from `fromPort` to `toPort`. -### IdString getPortClock(const CellInfo \*cell, IdString port) const +### TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const -Returns the clock input port for the specified output port. - -### bool isClockPort(const CellInfo \*cell, IdString port) const - -Returns true if the specified port is a clock input. +Return the _timing port class_ of a port. This can be a register or combinational input or output; clock input or +output; general startpoint or endpoint; or a port ignored for timing purposes. For register ports, clockPort is set +to the associated clock port. Placer Methods -------------- -- cgit v1.2.3 From 751335977fe7d69b23f6110ec4938408ec7a7ff8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 18:07:34 +0200 Subject: ice40: Add error for unknown cell type when getting timing info Signed-off-by: David Shah --- ice40/arch.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 888a0dee..5e56ea10 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -957,8 +957,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id if (port == id_glb_buf_out) return TMG_COMB_OUTPUT; return TMG_COMB_INPUT; + } else if (cell->type == id("SB_WARMBOOT")) { + return TMG_ENDPOINT; } - return TMG_IGNORE; + log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this)); } bool Arch::isGlobalNet(const NetInfo *net) const -- cgit v1.2.3 From ecc21caa77b09075a97d131890019f6b97281928 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 8 Aug 2018 18:14:22 +0200 Subject: move load/save proj to base --- gui/basewindow.cc | 33 +++++++++++++++++++++++++++++++++ gui/basewindow.h | 7 +++++-- gui/ice40/mainwindow.cc | 35 +---------------------------------- gui/ice40/mainwindow.h | 4 +--- 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 98dc9e20..98315ee9 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -31,6 +31,7 @@ #include "jsonparse.h" #include "log.h" #include "mainwindow.h" +#include "project.h" #include "pythontab.h" static void initBasenameResource() { Q_INIT_RESOURCE(base); } @@ -431,4 +432,36 @@ void BaseMainWindow::updateJsonLoaded() onJsonLoaded(); } +void BaseMainWindow::open_proj() +{ + QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj")); + if (!fileName.isEmpty()) { + try { + ProjectHandler proj; + disableActions(); + ctx = proj.load(fileName.toStdString()); + Q_EMIT contextChanged(ctx.get()); + log_info("Loaded project %s...\n", fileName.toStdString().c_str()); + updateJsonLoaded(); + onProjectLoaded(); + } catch (log_execution_error_exception) { + } + } +} + +void BaseMainWindow::save_proj() +{ + if (currentProj.empty()) { + QString fileName = QFileDialog::getSaveFileName(this, QString("Save Project"), QString(), QString("*.proj")); + if (fileName.isEmpty()) + return; + currentProj = fileName.toStdString(); + } + if (!currentProj.empty()) { + ProjectHandler proj; + proj.save(ctx.get(), currentProj); + } +} + + NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.h b/gui/basewindow.h index 67f39ac2..ef3e252c 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -56,6 +56,7 @@ class BaseMainWindow : public QMainWindow virtual void onDisableActions(){}; virtual void onJsonLoaded(){}; + virtual void onProjectLoaded(){}; virtual void onPackFinished(){}; virtual void onBudgetFinished(){}; virtual void onPlaceFinished(){}; @@ -66,8 +67,9 @@ class BaseMainWindow : public QMainWindow void closeTab(int index); virtual void new_proj() = 0; - virtual void open_proj() = 0; - virtual bool save_proj() = 0; + + void open_proj(); + void save_proj(); void open_json(); void budget(); @@ -93,6 +95,7 @@ class BaseMainWindow : public QMainWindow TaskManager *task; bool timing_driven; std::string currentJson; + std::string currentProj; // main widgets QTabWidget *tabWidget; diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index 9870cb0d..4a2eaaa9 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -29,7 +29,6 @@ #include "jsonparse.h" #include "log.h" #include "pcf.h" -#include "project.h" #include static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } @@ -166,23 +165,6 @@ void MainWindow::newContext(Context *ctx) setWindowTitle(title.c_str()); } -void MainWindow::open_proj() -{ - QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj")); - if (!fileName.isEmpty()) { - try { - ProjectHandler proj; - disableActions(); - ctx = proj.load(fileName.toStdString()); - Q_EMIT contextChanged(ctx.get()); - log_info("Loaded project %s...\n", fileName.toStdString().c_str()); - updateJsonLoaded(); - actionLoadPCF->setEnabled(false); - } catch (log_execution_error_exception) { - } - } -} - void MainWindow::open_pcf() { QString fileName = QFileDialog::getOpenFileName(this, QString("Open PCF"), QString(), QString("*.pcf")); @@ -191,22 +173,6 @@ void MainWindow::open_pcf() } } -bool MainWindow::save_proj() -{ - if (currentProj.empty()) { - QString fileName = QFileDialog::getSaveFileName(this, QString("Save Project"), QString(), QString("*.proj")); - if (fileName.isEmpty()) - return false; - currentProj = fileName.toStdString(); - } - if (!currentProj.empty()) { - ProjectHandler proj; - proj.save(ctx.get(), currentProj); - return true; - } - return false; -} - void MainWindow::save_asc() { QString fileName = QFileDialog::getSaveFileName(this, QString("Save ASC"), QString(), QString("*.asc")); @@ -227,5 +193,6 @@ void MainWindow::onDisableActions() void MainWindow::onJsonLoaded() { actionLoadPCF->setEnabled(true); } void MainWindow::onRouteFinished() { actionSaveAsc->setEnabled(true); } +void MainWindow::onProjectLoaded() { actionLoadPCF->setEnabled(false); } NEXTPNR_NAMESPACE_END diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h index c235f0a4..201bf1b1 100644 --- a/gui/ice40/mainwindow.h +++ b/gui/ice40/mainwindow.h @@ -41,11 +41,10 @@ class MainWindow : public BaseMainWindow void onDisableActions() override; void onJsonLoaded() override; void onRouteFinished() override; + void onProjectLoaded() override; protected Q_SLOTS: virtual void new_proj(); - virtual void open_proj(); - virtual bool save_proj(); void open_pcf(); void save_asc(); @@ -56,7 +55,6 @@ class MainWindow : public BaseMainWindow QAction *actionLoadPCF; QAction *actionSaveAsc; - std::string currentProj; std::string currentPCF; }; -- cgit v1.2.3 From fc5cee6fb896bc4d7a8b79dcde789e03c787bd89 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 8 Aug 2018 18:17:34 +0200 Subject: clangformat --- common/command.cc | 4 ++-- common/project.cc | 13 ++++++------- ecp5/project.cc | 8 +++----- generic/project.cc | 12 ++++-------- gui/basewindow.cc | 1 - gui/basewindow.h | 1 + gui/ecp5/mainwindow.cc | 12 +++++++----- gui/ecp5/mainwindow.h | 1 + gui/generic/mainwindow.cc | 3 ++- gui/ice40/mainwindow.cc | 2 +- ice40/project.cc | 8 ++++---- 11 files changed, 31 insertions(+), 34 deletions(-) diff --git a/common/command.cc b/common/command.cc index 9c2cc840..cf12df3a 100644 --- a/common/command.cc +++ b/common/command.cc @@ -35,8 +35,8 @@ #include "design_utils.h" #include "jsonparse.h" #include "log.h" +#include "timing.h" #include "version.h" -#include NEXTPNR_NAMESPACE_BEGIN @@ -244,7 +244,7 @@ int CommandHandler::exec() if (executeBeforeContext()) return 0; - + std::unique_ptr ctx; if (vm.count("load")) { ctx = project.load(vm["load"].as()); diff --git a/common/project.cc b/common/project.cc index 8e043f36..244ca761 100644 --- a/common/project.cc +++ b/common/project.cc @@ -17,12 +17,12 @@ * */ +#include "project.h" #include #include -#include "project.h" -#include "log.h" -#include "jsonparse.h" #include +#include "jsonparse.h" +#include "log.h" NEXTPNR_NAMESPACE_BEGIN @@ -34,10 +34,10 @@ void ProjectHandler::save(Context *ctx, std::string filename) 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)); -/* root.put("project.input.json", );*/ + /* root.put("project.input.json", );*/ root.put("project.params.freq", int(ctx->target_freq / 1e6)); root.put("project.params.seed", ctx->rngstate); - saveArch(ctx,root); + saveArch(ctx, root); pt::write_json(f, root); } @@ -55,7 +55,6 @@ std::unique_ptr ProjectHandler::load(std::string filename) if (version != 1) log_error("Wrong project format version.\n"); - ctx = createContext(root); std::string arch_name = root.get("project.arch.name"); @@ -77,7 +76,7 @@ std::unique_ptr ProjectHandler::load(std::string filename) if (params.count("seed")) ctx->rngseed(params.get("seed")); } - loadArch(ctx.get(),root, proj.parent_path().string()); + loadArch(ctx.get(), root, proj.parent_path().string()); } catch (...) { log_error("Error loading project file.\n"); } diff --git a/ecp5/project.cc b/ecp5/project.cc index a9b48bbe..6c1f6a94 100644 --- a/ecp5/project.cc +++ b/ecp5/project.cc @@ -17,11 +17,11 @@ * */ +#include "project.h" #include #include -#include "project.h" -#include "log.h" #include +#include "log.h" NEXTPNR_NAMESPACE_BEGIN @@ -50,8 +50,6 @@ std::unique_ptr ProjectHandler::createContext(pt::ptree &root) return std::unique_ptr(new Context(chipArgs)); } -void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) -{ -} +void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {} NEXTPNR_NAMESPACE_END diff --git a/generic/project.cc b/generic/project.cc index e412d8dc..8103e91f 100644 --- a/generic/project.cc +++ b/generic/project.cc @@ -17,16 +17,14 @@ * */ -#include #include "project.h" -#include "log.h" +#include #include +#include "log.h" NEXTPNR_NAMESPACE_BEGIN -void ProjectHandler::saveArch(Context *ctx, pt::ptree &root) -{ -} +void ProjectHandler::saveArch(Context *ctx, pt::ptree &root) {} std::unique_ptr ProjectHandler::createContext(pt::ptree &root) { @@ -34,8 +32,6 @@ std::unique_ptr ProjectHandler::createContext(pt::ptree &root) return std::unique_ptr(new Context(chipArgs)); } -void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) -{ -} +void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {} NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 98315ee9..857919ea 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -463,5 +463,4 @@ void BaseMainWindow::save_proj() } } - NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.h b/gui/basewindow.h index ef3e252c..6e8f8587 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -49,6 +49,7 @@ class BaseMainWindow : public QMainWindow virtual ~BaseMainWindow(); Context *getContext() { return ctx.get(); } void updateJsonLoaded(); + protected: void createMenusAndBars(); void disableActions(); diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc index efaad364..e5a5f1ba 100644 --- a/gui/ecp5/mainwindow.cc +++ b/gui/ecp5/mainwindow.cc @@ -29,7 +29,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN -MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) : BaseMainWindow(std::move(context), args, parent) +MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) + : BaseMainWindow(std::move(context), args, parent) { initMainResource(); @@ -50,7 +51,8 @@ void MainWindow::newContext(Context *ctx) setWindowTitle(title.c_str()); } -void MainWindow::createMenu() { +void MainWindow::createMenu() +{ // Add arch specific actions actionLoadBase = new QAction("Open Base Config", this); actionLoadBase->setIcon(QIcon(":/icons/resources/open_base.png")); @@ -71,7 +73,7 @@ void MainWindow::createMenu() { menuDesign->addSeparator(); menuDesign->addAction(actionLoadBase); - menuDesign->addAction(actionSaveConfig); + menuDesign->addAction(actionSaveConfig); } static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return ptr->get(); } @@ -96,8 +98,8 @@ static QStringList getSupportedPackages(ArchArgs::ArchArgsTypes chip) return packages; } - -void MainWindow::new_proj() { +void MainWindow::new_proj() +{ QMap arch; arch.insert("Lattice ECP5 25K", ArchArgs::LFE5U_25F); arch.insert("Lattice ECP5 45K", ArchArgs::LFE5U_45F); diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h index d1d5a5a2..9460913c 100644 --- a/gui/ecp5/mainwindow.h +++ b/gui/ecp5/mainwindow.h @@ -47,6 +47,7 @@ class MainWindow : public BaseMainWindow void newContext(Context *ctx); void open_base(); void save_config(); + private: QAction *actionLoadBase; QAction *actionSaveConfig; diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc index 050c0fb8..01eeb4ef 100644 --- a/gui/generic/mainwindow.cc +++ b/gui/generic/mainwindow.cc @@ -23,7 +23,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN -MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) : BaseMainWindow(std::move(context), args, parent) +MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) + : BaseMainWindow(std::move(context), args, parent) { initMainResource(); diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index 4a2eaaa9..35ad5768 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -24,12 +24,12 @@ #include #include #include +#include #include "bitstream.h" #include "design_utils.h" #include "jsonparse.h" #include "log.h" #include "pcf.h" -#include static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } diff --git a/ice40/project.cc b/ice40/project.cc index 53401d63..d1f1674a 100644 --- a/ice40/project.cc +++ b/ice40/project.cc @@ -17,10 +17,10 @@ * */ -#include #include "project.h" -#include "log.h" +#include #include +#include "log.h" #include "pcf.h" NEXTPNR_NAMESPACE_BEGIN @@ -28,7 +28,7 @@ NEXTPNR_NAMESPACE_BEGIN void ProjectHandler::saveArch(Context *ctx, pt::ptree &root) { root.put("project.arch.package", ctx->archArgs().package); - // if(!pcfFilename.empty()) + // if(!pcfFilename.empty()) // root.put("project.input.pcf", pcfFilename); } @@ -62,7 +62,7 @@ std::unique_ptr ProjectHandler::createContext(pt::ptree &root) 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("pcf"); + boost::filesystem::path pcf = boost::filesystem::path(path) / input.get("pcf"); std::ifstream f(pcf.string()); if (!apply_pcf(ctx, f)) log_error("Loading PCF failed.\n"); -- cgit v1.2.3 From 46aa56021be18d76523d89601ef98cf6d14b12c1 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 8 Aug 2018 18:34:12 +0200 Subject: Moved option to common --- common/command.cc | 1 + ice40/main.cc | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/common/command.cc b/common/command.cc index cf12df3a..736f6201 100644 --- a/common/command.cc +++ b/common/command.cc @@ -97,6 +97,7 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("seed", po::value(), "seed value for random number generator"); general.add_options()("slack_redist_iter", po::value(), "number of iterations between slack redistribution"); general.add_options()("cstrweight", po::value(), "placer weighting for relative constraint satisfaction"); + general.add_options()("pack-only", "pack design only without placement or routing"); general.add_options()("version,V", "show version"); general.add_options()("test", "check architecture database integrity"); diff --git a/ice40/main.cc b/ice40/main.cc index a98ca07e..2818a3ad 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -66,7 +66,6 @@ po::options_description Ice40CommandHandler::getArchOptions() specific.add_options()("asc", po::value(), "asc bitstream file to write"); specific.add_options()("read", po::value(), "asc bitstream file to read"); specific.add_options()("tmfuzz", "run path delay estimate fuzzer"); - specific.add_options()("pack-only", "pack design only without placement or routing"); return specific; } void Ice40CommandHandler::validate() -- cgit v1.2.3 From fc0131ceb0e10bc5560fb84a806bbf5fe1547e47 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 8 Aug 2018 18:41:30 +0200 Subject: cleanup --- gui/ecp5/mainwindow.cc | 4 ---- gui/ecp5/mainwindow.h | 2 -- gui/generic/mainwindow.cc | 4 ---- gui/generic/mainwindow.h | 2 -- 4 files changed, 12 deletions(-) diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc index e5a5f1ba..4fc8be16 100644 --- a/gui/ecp5/mainwindow.cc +++ b/gui/ecp5/mainwindow.cc @@ -126,10 +126,6 @@ void MainWindow::new_proj() } } -void MainWindow::open_proj() {} - -bool MainWindow::save_proj() { return false; } - void MainWindow::load_base_config(std::string filename) { disableActions(); diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h index 9460913c..aa1af17b 100644 --- a/gui/ecp5/mainwindow.h +++ b/gui/ecp5/mainwindow.h @@ -42,8 +42,6 @@ class MainWindow : public BaseMainWindow protected Q_SLOTS: virtual void new_proj(); - virtual void open_proj(); - virtual bool save_proj(); void newContext(Context *ctx); void open_base(); void save_config(); diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc index 01eeb4ef..76837ed4 100644 --- a/gui/generic/mainwindow.cc +++ b/gui/generic/mainwindow.cc @@ -49,8 +49,4 @@ void MainWindow::createMenu() {} void MainWindow::new_proj() {} -void MainWindow::open_proj() {} - -bool MainWindow::save_proj() { return false; } - NEXTPNR_NAMESPACE_END diff --git a/gui/generic/mainwindow.h b/gui/generic/mainwindow.h index 375436b6..a4ce9958 100644 --- a/gui/generic/mainwindow.h +++ b/gui/generic/mainwindow.h @@ -37,8 +37,6 @@ class MainWindow : public BaseMainWindow protected Q_SLOTS: virtual void new_proj(); - virtual void open_proj(); - virtual bool save_proj(); void newContext(Context *ctx); }; -- cgit v1.2.3 From 2390f7f59c05870845da349536cb384964cdb874 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 8 Aug 2018 18:46:33 +0200 Subject: Add ctx->settings Signed-off-by: Clifford Wolf --- common/nextpnr.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/nextpnr.h b/common/nextpnr.h index 3d9a66ca..92880089 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -371,6 +371,9 @@ struct BaseCtx mutable std::unordered_map *idstring_str_to_idx; mutable std::vector *idstring_idx_to_str; + // Project settings and config switches + std::unordered_map settings; + // Placed nets and cells. std::unordered_map> nets; std::unordered_map> cells; -- cgit v1.2.3