diff --git a/CMakeLists.txt b/CMakeLists.txt index ac58f169..8a64369f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ if (NOT Boost_PYTHON_FOUND ) message( FATAL_ERROR "No version of Boost::Python 3.x could be found.") endif () -include_directories(common/ gui/ frontend/json ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} 3rdparty/QtPropertyBrowser/src) +include_directories(common/ frontend/json ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) aux_source_directory(common/ COMMON_SRC_FILES) aux_source_directory(frontend/json/ JSON_PARSER_FILES) set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES}) @@ -115,7 +115,7 @@ foreach (family ${FAMILIES}) include(${family}/family.cmake) foreach (target ${family_targets}) # Include family-specific source files to all family targets and set defines appropriately - target_include_directories(${target} PRIVATE ${family}/ generated/) + target_include_directories(${target} PRIVATE ${family}/ generated/ gui/${family}/) target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family} QT_NO_KEYWORDS) target_link_libraries(${target} LINK_PUBLIC gui_${family} ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} ${GUI_LIBRARY_FILES_${ufamily}}) endforeach (target) diff --git a/README.md b/README.md index 50df309c..751a864b 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,6 @@ Prequisites - CMake 3.3 or later - Modern C++11 compiler (`clang-format` required for development) - - Note: clang may run out of memory building the chipdbs (peak memory - ~11GB) due to the system currently used. Use gcc, or the 1k-only - flag, if this causes a problem. - Qt5 or later (`qt5-default` for Ubuntu 16.04) - Python 3.5 or later, including development libraries (`python3-dev` for Ubuntu) - Boost libraries (`libboost-dev` or `libboost-all-dev` for Ubuntu) @@ -29,8 +26,8 @@ Building - For a release build, run `cmake .` - Add `-DCMAKE_INSTALL_PREFIX=/your/install/prefix` to use a different install prefix to the default `/usr/local` - Use Make to run the build itself - - For all targets, just run `make` - - For just the iCE40 CLI binary, run `make nextpnr-ice40` + - For all binary targets, just run `make` + - For just the iCE40 CLI&GUI binary, run `make nextpnr-ice40` - For just the iCE40 Python module, run `make nextpnrpy_ice40` - Using too many parallel jobs may lead to out-of-memory issues due to the significant memory needed to build the chipdbs - To install nextpnr, run `make install` diff --git a/common/design_utils.cc b/common/design_utils.cc index 4fbd16f1..92533fa3 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -64,11 +64,15 @@ void print_utilisation(const Context *ctx) for (auto bel : ctx->getBels()) { available_types[ctx->getBelType(bel)]++; } - log("\nDesign utilisation:\n"); + log_break(); + log_info("Device utilisation:\n"); for (auto type : available_types) { - log("\t%20s: %5d/%5d\n", ctx->belTypeToId(type.first).c_str(ctx), - get_or_default(used_types, type.first, 0), type.second); + IdString type_id = ctx->belTypeToId(type.first); + int used_bels = get_or_default(used_types, type.first, 0); + log_info("\t%20s: %5d/%5d %5d%%\n", type_id.c_str(ctx), used_bels, + type.second, 100 * used_bels / type.second); } + log_break(); } NEXTPNR_NAMESPACE_END diff --git a/gui/emb.cc b/common/emb.cc similarity index 100% rename from gui/emb.cc rename to common/emb.cc diff --git a/gui/emb.h b/common/emb.h similarity index 100% rename from gui/emb.h rename to common/emb.h diff --git a/common/log.cc b/common/log.cc index 2868e03f..495f83b1 100644 --- a/common/log.cc +++ b/common/log.cc @@ -150,12 +150,8 @@ void logv_error(const char *format, va_list ap) #ifdef EMSCRIPTEN log_files = backup_log_files; - throw 0; -#elif defined(_MSC_VER) - _exit(EXIT_FAILURE); -#else - _Exit(EXIT_FAILURE); #endif + throw log_execution_error_exception(); } void log(const char *format, ...) @@ -212,7 +208,7 @@ void log_cmd_error(const char *format, ...) logv_error(format, ap); } -void log_spacer() +void log_break() { if (log_newline_count < 2) log("\n"); @@ -220,12 +216,6 @@ void log_spacer() log("\n"); } -void log_push() {} - -void log_pop() { log_flush(); } - -void log_reset_stack() { log_flush(); } - void log_flush() { for (auto f : log_files) @@ -235,8 +225,4 @@ void log_flush() f->flush(); } -void log_cell(CellInfo *cell, std::string indent) {} - -void log_net(NetInfo *net, std::string indent) {} - NEXTPNR_NAMESPACE_END diff --git a/common/log.h b/common/log.h index 597b5fac..65b3f178 100644 --- a/common/log.h +++ b/common/log.h @@ -43,6 +43,10 @@ struct log_cmd_error_exception { }; +struct log_execution_error_exception +{ +}; + extern std::vector log_files; extern std::vector log_streams; extern FILE *log_errfile; @@ -71,25 +75,9 @@ NXP_NORETURN void log_error(const char *format, ...) NXP_NORETURN void log_cmd_error(const char *format, ...) NXP_ATTRIBUTE(format(printf, 1, 2), noreturn); -void log_spacer(); -void log_push(); -void log_pop(); - -void log_backtrace(const char *prefix, int levels); -void log_reset_stack(); +void log_break(); void log_flush(); -/* -const char *log_id(RTLIL::IdString id); - -template static inline const char *log_id(T *obj) { - return log_id(obj->name); -} -*/ - -void log_cell(CellInfo *cell, std::string indent = ""); -void log_net(NetInfo *net, std::string indent = ""); - #ifndef NDEBUG static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index b093d855..b24f66ea 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -53,4 +53,126 @@ void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx) ctx->idstring_idx_to_str->push_back(&insert_rc.first->first); } +static uint32_t xorshift32(uint32_t x) +{ + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return x; +} + +uint32_t Context::checksum() const +{ + uint32_t cksum = xorshift32(123456789); + + uint32_t cksum_nets_sum = 0; + for (auto &it : nets) { + auto &ni = *it.second; + uint32_t x = 123456789; + x = xorshift32(x + xorshift32(it.first.index)); + x = xorshift32(x + xorshift32(ni.name.index)); + if (ni.driver.cell) + x = xorshift32(x + xorshift32(ni.driver.cell->name.index)); + x = xorshift32(x + xorshift32(ni.driver.port.index)); + x = xorshift32(x + xorshift32(getDelayChecksum(ni.driver.budget))); + + for (auto &u : ni.users) { + if (u.cell) + x = xorshift32(x + xorshift32(u.cell->name.index)); + x = xorshift32(x + xorshift32(u.port.index)); + x = xorshift32(x + xorshift32(getDelayChecksum(u.budget))); + } + + uint32_t attr_x_sum = 0; + for (auto &a : ni.attrs) { + uint32_t attr_x = 123456789; + attr_x = xorshift32(attr_x + xorshift32(a.first.index)); + for (uint8_t ch : a.second) + attr_x = xorshift32(attr_x + xorshift32(ch)); + attr_x_sum += attr_x; + } + x = xorshift32(x + xorshift32(attr_x_sum)); + + uint32_t wire_x_sum = 0; + for (auto &w : ni.wires) { + uint32_t wire_x = 123456789; + wire_x = xorshift32(wire_x + xorshift32(getWireChecksum(w.first))); + wire_x = xorshift32(wire_x + xorshift32(getPipChecksum(w.second))); + wire_x_sum += wire_x; + } + x = xorshift32(x + xorshift32(wire_x_sum)); + + uint32_t pip_x_sum = 0; + for (auto &p : ni.pips) { + uint32_t pip_x = 123456789; + pip_x = xorshift32(pip_x + xorshift32(getPipChecksum(p.first))); + pip_x = xorshift32(pip_x + xorshift32(p.second)); + pip_x_sum += pip_x; + } + x = xorshift32(x + xorshift32(pip_x_sum)); + + cksum_nets_sum += x; + } + cksum = xorshift32(cksum + xorshift32(cksum_nets_sum)); + + uint32_t cksum_cells_sum = 0; + for (auto &it : cells) { + auto &ci = *it.second; + uint32_t x = 123456789; + x = xorshift32(x + xorshift32(it.first.index)); + x = xorshift32(x + xorshift32(ci.name.index)); + x = xorshift32(x + xorshift32(ci.type.index)); + + uint32_t port_x_sum = 0; + for (auto &p : ci.ports) { + uint32_t port_x = 123456789; + port_x = xorshift32(port_x + xorshift32(p.first.index)); + port_x = xorshift32(port_x + xorshift32(p.second.name.index)); + if (p.second.net) + port_x = xorshift32(port_x + + xorshift32(p.second.net->name.index)); + port_x = xorshift32(port_x + xorshift32(p.second.type)); + port_x_sum += port_x; + } + x = xorshift32(x + xorshift32(port_x_sum)); + + uint32_t attr_x_sum = 0; + for (auto &a : ci.attrs) { + uint32_t attr_x = 123456789; + attr_x = xorshift32(attr_x + xorshift32(a.first.index)); + for (uint8_t ch : a.second) + attr_x = xorshift32(attr_x + xorshift32(ch)); + attr_x_sum += attr_x; + } + x = xorshift32(x + xorshift32(attr_x_sum)); + + uint32_t param_x_sum = 0; + for (auto &p : ci.params) { + uint32_t param_x = 123456789; + param_x = xorshift32(param_x + xorshift32(p.first.index)); + for (uint8_t ch : p.second) + param_x = xorshift32(param_x + xorshift32(ch)); + param_x_sum += param_x; + } + x = xorshift32(x + xorshift32(param_x_sum)); + + x = xorshift32(x + xorshift32(getBelChecksum(ci.bel))); + x = xorshift32(x + xorshift32(ci.belStrength)); + + uint32_t pin_x_sum = 0; + for (auto &a : ci.pins) { + uint32_t pin_x = 123456789; + pin_x = xorshift32(pin_x + xorshift32(a.first.index)); + pin_x = xorshift32(pin_x + xorshift32(a.second.index)); + pin_x_sum += pin_x; + } + x = xorshift32(x + xorshift32(pin_x_sum)); + + cksum_cells_sum += x; + } + cksum = xorshift32(cksum + xorshift32(cksum_cells_sum)); + + return cksum; +} + NEXTPNR_NAMESPACE_END diff --git a/common/nextpnr.h b/common/nextpnr.h index 5ccbf057..1cf0cbbb 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -208,7 +208,7 @@ struct PortRef { CellInfo *cell = nullptr; IdString port; - delay_t budget; + delay_t budget = 0; }; struct NetInfo @@ -289,6 +289,7 @@ NEXTPNR_NAMESPACE_BEGIN struct Context : Arch { bool verbose = false; + bool debug = false; bool force = false; Context(ArchArgs args) : Arch(args) {} @@ -301,10 +302,14 @@ struct Context : Arch { // xorshift64star // https://arxiv.org/abs/1402.6246 + + uint64_t retval = rngstate * 0x2545F4914F6CDD1D; + rngstate ^= rngstate >> 12; rngstate ^= rngstate << 25; rngstate ^= rngstate >> 27; - return rngstate * 0x2545F4914F6CDD1D; + + return retval; } int rng() { return rng64() & 0x3fffffff; } @@ -350,6 +355,8 @@ struct Context : Arch std::sort(a.begin(), a.end()); shuffle(a); } + + uint32_t checksum() const; }; NEXTPNR_NAMESPACE_END diff --git a/common/place_sa.cc b/common/place_sa.cc index 2b8b960b..058f0a84 100644 --- a/common/place_sa.cc +++ b/common/place_sa.cc @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -42,6 +41,8 @@ NEXTPNR_NAMESPACE_BEGIN +typedef int64_t wirelen_t; + class SAPlacer { public: @@ -76,6 +77,8 @@ class SAPlacer bool place() { + log_break(); + size_t placed_cells = 0; // Initial constraints placer for (auto cell_entry : ctx->cells) { @@ -143,9 +146,10 @@ class SAPlacer // Calculate wirelength after initial placement curr_wirelength = 0; + curr_tns = 0; for (auto net : ctx->nets) { - float wl = get_wirelength(net.second); - wirelengths[net.second] = wl; + wirelen_t wl = get_wirelength(net.second, curr_tns); + wirelengths[net.first] = wl; curr_wirelength += wl; } @@ -159,8 +163,9 @@ class SAPlacer improved = false; if (iter % 5 == 0 || iter == 1) - log_info(" at iteration #%d: temp = %f, wire length = %f\n", - iter, temp, curr_wirelength); + log_info(" at iteration #%d: temp = %f, wire length = " + "%.0f, est tns = %.02fns\n", + iter, temp, double(curr_wirelength), curr_tns); for (int m = 0; m < 15; ++m) { // Loop through all automatically placed cells @@ -183,7 +188,7 @@ class SAPlacer if (iter % 5 != 0) log_info( " at iteration #%d: temp = %f, wire length = %f\n", - iter, temp, curr_wirelength); + iter, temp, double(curr_wirelength)); break; } @@ -213,6 +218,16 @@ class SAPlacer temp *= 0.8; } } + + // Recalculate total wirelength entirely to avoid rounding errors + // accumulating over time + curr_wirelength = 0; + curr_tns = 0; + for (auto net : ctx->nets) { + wirelen_t wl = get_wirelength(net.second, curr_tns); + wirelengths[net.first] = wl; + curr_wirelength += wl; + } } // Final post-pacement validitiy check for (auto bel : ctx->getBels()) { @@ -221,9 +236,17 @@ class SAPlacer std::string cell_text = "no cell"; if (cell != IdString()) cell_text = std::string("cell '") + cell.str(ctx) + "'"; - log_error("post-placement validity check failed for Bel '%s' " - "(%s)", - ctx->getBelName(bel).c_str(ctx), cell_text.c_str()); + if (ctx->force) { + log_warning( + "post-placement validity check failed for Bel '%s' " + "(%s)\n", + ctx->getBelName(bel).c_str(ctx), cell_text.c_str()); + } else { + log_error( + "post-placement validity check failed for Bel '%s' " + "(%s)\n", + ctx->getBelName(bel).c_str(ctx), cell_text.c_str()); + } } } return true; @@ -288,9 +311,9 @@ class SAPlacer } // Get the total estimated wirelength for a net - float get_wirelength(NetInfo *net) + wirelen_t get_wirelength(NetInfo *net, float &tns) { - float wirelength = 0; + wirelen_t wirelength = 0; int driver_x, driver_y; bool driver_gb; CellInfo *driver_cell = net->driver.cell; @@ -303,24 +326,36 @@ class SAPlacer driver_cell->bel, ctx->portPinFromId(net->driver.port)); if (driver_gb) return 0; + float worst_slack = 1000; + int xmin = driver_x, xmax = driver_x, ymin = driver_y, ymax = driver_y; for (auto load : net->users) { if (load.cell == nullptr) continue; CellInfo *load_cell = load.cell; if (load_cell->bel == BelId()) continue; - // ctx->estimatePosition(load_cell->bel, load_x, load_y, load_gb); + WireId user_wire = ctx->getWireBelPin( load_cell->bel, ctx->portPinFromId(load.port)); - // wirelength += std::abs(load_x - driver_x) + std::abs(load_y - - // driver_y); delay_t raw_wl = ctx->estimateDelay(drv_wire, user_wire); - wirelength += pow(1.3, (ctx->getDelayNS(raw_wl) - - ctx->getDelayNS(load.budget)) / - 10) + - ctx->getDelayNS(raw_wl); - // wirelength += pow(ctx->estimateDelay(drv_wire, user_wire), 2.0); + float slack = + ctx->getDelayNS(load.budget) - ctx->getDelayNS(raw_wl); + if (slack < 0) + tns += slack; + worst_slack = std::min(slack, worst_slack); + int load_x, load_y; + bool load_gb; + ctx->estimatePosition(load_cell->bel, load_x, load_y, load_gb); + if (load_gb) + continue; + xmin = std::min(xmin, load_x); + ymin = std::min(ymin, load_y); + xmax = std::max(xmax, load_x); + ymax = std::max(ymax, load_y); } + wirelength = + wirelen_t((((ymax - ymin) + (xmax - xmin)) * + std::min(5.0, (1.0 + std::exp(-worst_slack / 5))))); return wirelength; } @@ -328,13 +363,13 @@ class SAPlacer bool try_swap_position(CellInfo *cell, BelId newBel) { static std::unordered_set update; - static std::vector> new_lengths; + static std::vector> new_lengths; new_lengths.clear(); update.clear(); BelId oldBel = cell->bel; IdString other = ctx->getBelCell(newBel, true); CellInfo *other_cell = nullptr; - float new_wirelength = 0, delta; + wirelen_t new_wirelength = 0, delta; ctx->unbindBel(oldBel); if (other != IdString()) { other_cell = ctx->cells[other]; @@ -373,10 +408,11 @@ class SAPlacer // Recalculate wirelengths for all nets touched by the peturbation for (auto net : update) { - new_wirelength -= wirelengths.at(net); - float net_new_wl = get_wirelength(net); + new_wirelength -= wirelengths.at(net->name); + float temp_tns = 0; + wirelen_t net_new_wl = get_wirelength(net, temp_tns); new_wirelength += net_new_wl; - new_lengths.push_back(std::make_pair(net, net_new_wl)); + new_lengths.push_back(std::make_pair(net->name, net_new_wl)); } delta = new_wirelength - curr_wirelength; n_move++; @@ -384,7 +420,7 @@ class SAPlacer if (delta < 0 || (temp > 1e-6 && (ctx->rng() / float(0x3fffffff)) <= std::exp(-delta / temp))) { n_accept++; - if (delta < 0) + if (delta < 2) improved = true; } else { if (other != IdString()) @@ -434,8 +470,9 @@ class SAPlacer } Context *ctx; - std::unordered_map wirelengths; - float curr_wirelength = std::numeric_limits::infinity(); + std::unordered_map wirelengths; + wirelen_t curr_wirelength = std::numeric_limits::max(); + float curr_tns = 0; float temp = 1000; bool improved = false; int n_move, n_accept; @@ -448,9 +485,14 @@ class SAPlacer bool place_design_sa(Context *ctx) { - SAPlacer placer(ctx); - placer.place(); - return true; + try { + SAPlacer placer(ctx); + placer.place(); + log_info("Checksum: 0x%08x\n", ctx->checksum()); + return true; + } catch (log_execution_error_exception) { + return false; + } } NEXTPNR_NAMESPACE_END diff --git a/common/pybindings.cc b/common/pybindings.cc index 26e8bf69..02dc2395 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -52,8 +52,7 @@ void parse_json_shim(std::string filename, Context &d) std::ifstream inf(filename); if (!inf) throw std::runtime_error("failed to open file " + filename); - std::istream *ifp = &inf; - parse_json_file(ifp, filename, &d); + parse_json_file(inf, filename, &d); } // Create a new Chip and load design from json file diff --git a/common/route.cc b/common/route.cc index 6a410397..967f9aa1 100644 --- a/common/route.cc +++ b/common/route.cc @@ -27,6 +27,29 @@ namespace { USING_NEXTPNR_NAMESPACE +struct hash_id_wire +{ + std::size_t operator()(const std::pair &arg) const + noexcept + { + std::size_t seed = std::hash()(arg.first); + seed ^= std::hash()(arg.second) + 0x9e3779b9 + (seed << 6) + + (seed >> 2); + return seed; + } +}; + +struct hash_id_pip +{ + std::size_t operator()(const std::pair &arg) const noexcept + { + std::size_t seed = std::hash()(arg.first); + seed ^= std::hash()(arg.second) + 0x9e3779b9 + (seed << 6) + + (seed >> 2); + return seed; + } +}; + struct QueuedWire { WireId wire; @@ -46,6 +69,13 @@ struct QueuedWire }; }; +struct RipupScoreboard +{ + std::unordered_map, int, hash_id_wire> + wireScores; + std::unordered_map, int, hash_id_pip> pipScores; +}; + void ripup_net(Context *ctx, IdString net_name) { auto net_info = ctx->nets.at(net_name); @@ -62,13 +92,15 @@ void ripup_net(Context *ctx, IdString net_name) struct Router { Context *ctx; + RipupScoreboard scores; IdString net_name; + bool ripup; delay_t ripup_penalty; std::unordered_set rippedNets; std::unordered_map visited; - int visitCnt = 0, revisitCnt = 0; + int visitCnt = 0, revisitCnt = 0, overtimeRevisitCnt = 0; bool routedOkay = false; delay_t maxDelay = 0.0; WireId failedDest; @@ -94,36 +126,53 @@ struct Router visited[qw.wire] = qw; } - while (!queue.empty() && !visited.count(dst_wire)) { + int thisVisitCnt = 0; + int thisVisitCntLimit = 0; + + while (!queue.empty() && + (thisVisitCntLimit == 0 || thisVisitCnt < thisVisitCntLimit)) { QueuedWire qw = queue.top(); queue.pop(); + if (thisVisitCntLimit == 0 && visited.count(dst_wire)) + thisVisitCntLimit = (thisVisitCnt * 3) / 2; + for (auto pip : ctx->getPipsDownhill(qw.wire)) { - delay_t next_delay = qw.delay; - IdString ripupNet = net_name; - visitCnt++; - - if (!ctx->checkPipAvail(pip)) { - if (!ripup) - continue; - ripupNet = ctx->getPipNet(pip, true); - if (ripupNet == net_name) - continue; - } - + delay_t next_delay = + qw.delay + ctx->getPipDelay(pip).avgDelay(); WireId next_wire = ctx->getPipDstWire(pip); - next_delay += ctx->getPipDelay(pip).avgDelay(); + bool foundRipupNet = false; + thisVisitCnt++; if (!ctx->checkWireAvail(next_wire)) { if (!ripup) continue; - ripupNet = ctx->getWireNet(next_wire, true); - if (ripupNet == net_name) + IdString ripupWireNet = ctx->getWireNet(next_wire, true); + if (ripupWireNet == net_name || ripupWireNet == IdString()) continue; + auto it = scores.wireScores.find( + std::make_pair(ripupWireNet, next_wire)); + if (it != scores.wireScores.end()) + next_delay += it->second * ripup_penalty; + foundRipupNet = true; } - if (ripupNet != net_name) + if (!ctx->checkPipAvail(pip)) { + if (!ripup) + continue; + IdString ripupPipNet = ctx->getPipNet(pip, true); + if (ripupPipNet == net_name || ripupPipNet == IdString()) + continue; + auto it = scores.pipScores.find( + std::make_pair(ripupPipNet, pip)); + if (it != scores.pipScores.end()) + next_delay += it->second * ripup_penalty; + foundRipupNet = true; + } + + if (foundRipupNet) next_delay += ripup_penalty; + assert(next_delay >= 0); if (visited.count(next_wire)) { @@ -131,14 +180,17 @@ struct Router next_delay + ctx->getDelayEpsilon()) continue; #if 0 // FIXME - if (ctx->verbose) + if (ctx->debug) log("Found better route to %s. Old vs new delay " "estimate: %.3f %.3f\n", ctx->getWireName(next_wire).c_str(), ctx->getDelayNS(visited.at(next_wire).delay), ctx->getDelayNS(next_delay)); #endif - revisitCnt++; + if (thisVisitCntLimit == 0) + revisitCnt++; + else + overtimeRevisitCnt++; } QueuedWire next_qw; @@ -152,18 +204,21 @@ struct Router queue.push(next_qw); } } + + visitCnt += thisVisitCnt; } - Router(Context *ctx, WireId src_wire, WireId dst_wire, bool ripup = false, - delay_t ripup_penalty = 0) - : ctx(ctx), ripup(ripup), ripup_penalty(ripup_penalty) + Router(Context *ctx, RipupScoreboard &scores, WireId src_wire, + WireId dst_wire, bool ripup = false, delay_t ripup_penalty = 0) + : ctx(ctx), scores(scores), ripup(ripup), + ripup_penalty(ripup_penalty) { std::unordered_map src_wires; src_wires[src_wire] = 0; route(src_wires, dst_wire); routedOkay = visited.count(dst_wire); - if (ctx->verbose) { + if (ctx->debug) { log("Route (from destination to source):\n"); WireId cursor = dst_wire; @@ -180,17 +235,17 @@ struct Router } } - Router(Context *ctx, IdString net_name, bool ripup = false, - delay_t ripup_penalty = 0) - : ctx(ctx), net_name(net_name), ripup(ripup), + Router(Context *ctx, RipupScoreboard &scores, IdString net_name, + bool ripup = false, delay_t ripup_penalty = 0) + : ctx(ctx), scores(scores), net_name(net_name), ripup(ripup), ripup_penalty(ripup_penalty) { auto net_info = ctx->nets.at(net_name); - if (ctx->verbose) + if (ctx->debug) log("Routing net %s.\n", net_name.c_str(ctx)); - if (ctx->verbose) + if (ctx->debug) log(" Source: %s.%s.\n", net_info->driver.cell->name.c_str(ctx), net_info->driver.port.c_str(ctx)); @@ -201,7 +256,7 @@ struct Router net_info->driver.cell->name.c_str(ctx), net_info->driver.cell->type.c_str(ctx)); - if (ctx->verbose) + if (ctx->debug) log(" Source bel: %s\n", ctx->getBelName(src_bel).c_str(ctx)); IdString driver_port = net_info->driver.port; @@ -220,7 +275,7 @@ struct Router net_info->driver.cell->name.c_str(ctx), ctx->getBelName(src_bel).c_str(ctx)); - if (ctx->verbose) + if (ctx->debug) log(" Source wire: %s\n", ctx->getWireName(src_wire).c_str(ctx)); std::unordered_map src_wires; @@ -232,7 +287,7 @@ struct Router ctx->shuffle(users_array); for (auto &user_it : users_array) { - if (ctx->verbose) + if (ctx->debug) log(" Route to: %s.%s.\n", user_it.cell->name.c_str(ctx), user_it.port.c_str(ctx)); @@ -243,7 +298,7 @@ struct Router user_it.cell->name.c_str(ctx), user_it.cell->type.c_str(ctx)); - if (ctx->verbose) + if (ctx->debug) log(" Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx)); @@ -264,7 +319,7 @@ struct Router user_it.cell->name.c_str(ctx), ctx->getBelName(dst_bel).c_str(ctx)); - if (ctx->verbose) { + if (ctx->debug) { log(" Destination wire: %s\n", ctx->getWireName(dst_wire).c_str(ctx)); log(" Path delay estimate: %.2f\n", @@ -274,7 +329,7 @@ struct Router route(src_wires, dst_wire); if (visited.count(dst_wire) == 0) { - if (ctx->verbose) + if (ctx->debug) log("Failed to route %s -> %s.\n", ctx->getWireName(src_wire).c_str(ctx), ctx->getWireName(dst_wire).c_str(ctx)); @@ -287,18 +342,18 @@ struct Router return; } - if (ctx->verbose) + if (ctx->debug) log(" Final path delay: %.3f\n", ctx->getDelayNS(visited[dst_wire].delay)); maxDelay = fmaxf(maxDelay, visited[dst_wire].delay); - if (ctx->verbose) + if (ctx->debug) log(" Route (from destination to source):\n"); WireId cursor = dst_wire; while (1) { - if (ctx->verbose) + if (ctx->debug) log(" %8.3f %s\n", ctx->getDelayNS(visited[cursor].delay), ctx->getWireName(cursor).c_str(ctx)); @@ -306,22 +361,31 @@ struct Router if (src_wires.count(cursor)) break; - IdString conflicting_net = ctx->getWireNet(cursor, true); + IdString conflicting_wire_net = ctx->getWireNet(cursor, true); + IdString conflicting_pip_net = + ctx->getPipNet(visited[cursor].pip, true); - if (conflicting_net != IdString()) { + if (conflicting_wire_net != IdString()) { assert(ripup); - assert(conflicting_net != net_name); - ripup_net(ctx, conflicting_net); - rippedNets.insert(conflicting_net); + assert(conflicting_wire_net != net_name); + ripup_net(ctx, conflicting_wire_net); + rippedNets.insert(conflicting_wire_net); + scores.wireScores[std::make_pair(net_name, cursor)]++; + scores.wireScores[std::make_pair(conflicting_wire_net, + cursor)]++; } - conflicting_net = ctx->getPipNet(visited[cursor].pip, true); - - if (conflicting_net != IdString()) { + if (conflicting_pip_net != IdString()) { assert(ripup); - assert(conflicting_net != net_name); - ripup_net(ctx, conflicting_net); - rippedNets.insert(conflicting_net); + assert(conflicting_pip_net != net_name); + if (conflicting_wire_net != conflicting_pip_net) { + ripup_net(ctx, conflicting_pip_net); + rippedNets.insert(conflicting_pip_net); + } + scores.pipScores[std::make_pair(net_name, + visited[cursor].pip)]++; + scores.pipScores[std::make_pair(conflicting_pip_net, + visited[cursor].pip)]++; } net_info->wires[cursor] = visited[cursor].pip; @@ -343,210 +407,254 @@ NEXTPNR_NAMESPACE_BEGIN bool route_design(Context *ctx) { - delay_t ripup_penalty = 5; + try { + delay_t ripup_penalty = ctx->getRipupDelayPenalty(); + RipupScoreboard scores; - log_info("Routing..\n"); + log_break(); + log_info("Routing..\n"); - std::unordered_set netsQueue; + std::unordered_set netsQueue; - for (auto &net_it : ctx->nets) { - auto net_name = net_it.first; - auto net_info = net_it.second; + for (auto &net_it : ctx->nets) { + auto net_name = net_it.first; + auto net_info = net_it.second; - if (net_info->driver.cell == nullptr) - continue; - - if (!net_info->wires.empty()) - continue; - - netsQueue.insert(net_name); - } - - if (netsQueue.empty()) { - log_info("found no unrouted nets. no routing necessary.\n"); - return true; - } - - log_info("found %d unrouted nets. starting routing procedure.\n", - int(netsQueue.size())); - - delay_t estimatedTotalDelay = 0.0; - int estimatedTotalDelayCnt = 0; - - for (auto net_name : netsQueue) { - auto net_info = ctx->nets.at(net_name); - - auto src_bel = net_info->driver.cell->bel; - - if (src_bel == BelId()) - continue; - - IdString driver_port = net_info->driver.port; - - auto driver_port_it = net_info->driver.cell->pins.find(driver_port); - if (driver_port_it != net_info->driver.cell->pins.end()) - driver_port = driver_port_it->second; - - auto src_wire = - ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port)); - - if (src_wire == WireId()) - continue; - - for (auto &user_it : net_info->users) { - auto dst_bel = user_it.cell->bel; - - if (dst_bel == BelId()) + if (net_info->driver.cell == nullptr) continue; - IdString user_port = user_it.port; - - auto user_port_it = user_it.cell->pins.find(user_port); - - if (user_port_it != user_it.cell->pins.end()) - user_port = user_port_it->second; - - auto dst_wire = - ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port)); - - if (dst_wire == WireId()) + if (!net_info->wires.empty()) continue; - estimatedTotalDelay += ctx->estimateDelay(src_wire, dst_wire); - estimatedTotalDelayCnt++; + netsQueue.insert(net_name); } - } - log_info("estimated total wire delay: %.2f (avg %.2f)\n", - float(estimatedTotalDelay), - float(estimatedTotalDelay) / estimatedTotalDelayCnt); - - int iterCnt = 0; - - while (!netsQueue.empty()) { - if (iterCnt == 200) { - log_info("giving up after %d iterations.\n", iterCnt); - return false; + if (netsQueue.empty()) { + log_info("found no unrouted nets. no routing necessary.\n"); + return true; } - log_info("-- %d --\n", ++iterCnt); - int visitCnt = 0, revisitCnt = 0, netCnt = 0; + log_info("found %d unrouted nets. starting routing procedure.\n", + int(netsQueue.size())); - std::unordered_set ripupQueue; + delay_t estimatedTotalDelay = 0.0; + int estimatedTotalDelayCnt = 0; - log_info("routing queue contains %d nets.\n", int(netsQueue.size())); - bool printNets = netsQueue.size() < 10; + for (auto net_name : netsQueue) { + auto net_info = ctx->nets.at(net_name); - std::vector netsArray(netsQueue.begin(), netsQueue.end()); - ctx->sorted_shuffle(netsArray); - netsQueue.clear(); + auto src_bel = net_info->driver.cell->bel; - for (auto net_name : netsArray) { - if (printNets) - log_info(" routing net %s. (%d users)\n", net_name.c_str(ctx), - int(ctx->nets.at(net_name)->users.size())); + if (src_bel == BelId()) + continue; - Router router(ctx, net_name, false); + IdString driver_port = net_info->driver.port; - netCnt++; - visitCnt += router.visitCnt; - revisitCnt += router.revisitCnt; + auto driver_port_it = net_info->driver.cell->pins.find(driver_port); + if (driver_port_it != net_info->driver.cell->pins.end()) + driver_port = driver_port_it->second; - if (!router.routedOkay) { - if (printNets) - log_info(" failed to route to %s.\n", - ctx->getWireName(router.failedDest).c_str(ctx)); - ripupQueue.insert(net_name); + auto src_wire = ctx->getWireBelPin(src_bel, + ctx->portPinFromId(driver_port)); + + if (src_wire == WireId()) + continue; + + for (auto &user_it : net_info->users) { + auto dst_bel = user_it.cell->bel; + + if (dst_bel == BelId()) + continue; + + IdString user_port = user_it.port; + + auto user_port_it = user_it.cell->pins.find(user_port); + + if (user_port_it != user_it.cell->pins.end()) + user_port = user_port_it->second; + + auto dst_wire = ctx->getWireBelPin( + dst_bel, ctx->portPinFromId(user_port)); + + if (dst_wire == WireId()) + continue; + + estimatedTotalDelay += ctx->estimateDelay(src_wire, dst_wire); + estimatedTotalDelayCnt++; + } + } + + log_info("estimated total wire delay: %.2f (avg %.2f)\n", + float(estimatedTotalDelay), + float(estimatedTotalDelay) / estimatedTotalDelayCnt); + + int iterCnt = 0; + + while (!netsQueue.empty()) { + if (iterCnt == 200) { + log_warning("giving up after %d iterations.\n", iterCnt); + log_info("Checksum: 0x%08x\n", ctx->checksum()); + return false; } - if (!printNets && netCnt % 100 == 0) - log_info(" processed %d nets. (%d routed, %d failed)\n", - netCnt, netCnt - int(ripupQueue.size()), - int(ripupQueue.size())); - } + iterCnt++; + if (ctx->verbose) + log_info("-- %d --\n", iterCnt); - if (netCnt % 100 != 0) - log_info(" processed %d nets. (%d routed, %d failed)\n", netCnt, - netCnt - int(ripupQueue.size()), int(ripupQueue.size())); - log_info(" routing pass visited %d PIPs (%.2f%% revisits).\n", - visitCnt, (100.0 * revisitCnt) / visitCnt); + int visitCnt = 0, revisitCnt = 0, overtimeRevisitCnt = 0, + netCnt = 0; - if (!ripupQueue.empty()) { - log_info("failed to route %d nets. re-routing in ripup mode.\n", - int(ripupQueue.size())); + std::unordered_set ripupQueue; - printNets = ripupQueue.size() < 10; + if (ctx->verbose || iterCnt == 1) + log_info("routing queue contains %d nets.\n", + int(netsQueue.size())); - visitCnt = 0; - revisitCnt = 0; - netCnt = 0; - int ripCnt = 0; + bool printNets = ctx->verbose && (netsQueue.size() < 10); - std::vector ripupArray(ripupQueue.begin(), - ripupQueue.end()); - ctx->sorted_shuffle(ripupArray); + std::vector netsArray(netsQueue.begin(), netsQueue.end()); + ctx->sorted_shuffle(netsArray); + netsQueue.clear(); - for (auto net_name : ripupArray) { + for (auto net_name : netsArray) { if (printNets) log_info(" routing net %s. (%d users)\n", net_name.c_str(ctx), int(ctx->nets.at(net_name)->users.size())); - Router router(ctx, net_name, true, - ripup_penalty * (iterCnt - 1)); + Router router(ctx, scores, net_name, false); netCnt++; visitCnt += router.visitCnt; revisitCnt += router.revisitCnt; + overtimeRevisitCnt += router.overtimeRevisitCnt; - if (!router.routedOkay) - log_error("Net %s is impossible to route.\n", - net_name.c_str(ctx)); - - for (auto it : router.rippedNets) - netsQueue.insert(it); - - if (printNets) { - if (router.rippedNets.size() < 10) { - log_info(" ripped up %d other nets:\n", - int(router.rippedNets.size())); - for (auto n : router.rippedNets) - log_info(" %s (%d users)\n", n.c_str(ctx), - int(ctx->nets.at(n)->users.size())); - } else { - log_info(" ripped up %d other nets.\n", - int(router.rippedNets.size())); - } + if (!router.routedOkay) { + if (printNets) + log_info( + " failed to route to %s.\n", + ctx->getWireName(router.failedDest).c_str(ctx)); + ripupQueue.insert(net_name); } - ripCnt += router.rippedNets.size(); - - if (!printNets && netCnt % 100 == 0) - log_info(" routed %d nets, ripped %d nets.\n", netCnt, - ripCnt); + if ((ctx->verbose || iterCnt == 1) && !printNets && + (netCnt % 100 == 0)) + log_info(" processed %d nets. (%d routed, %d failed)\n", + netCnt, netCnt - int(ripupQueue.size()), + int(ripupQueue.size())); } - if (netCnt % 100 != 0) - log_info(" routed %d nets, ripped %d nets.\n", netCnt, ripCnt); + int normalRouteCnt = netCnt - int(ripupQueue.size()); - log_info(" routing pass visited %d PIPs (%.2f%% revisits).\n", - visitCnt, (100.0 * revisitCnt) / visitCnt); + if ((ctx->verbose || iterCnt == 1) && (netCnt % 100 != 0)) + log_info(" processed %d nets. (%d routed, %d failed)\n", + netCnt, normalRouteCnt, int(ripupQueue.size())); - if (!netsQueue.empty()) - log_info(" ripped up %d previously routed nets. continue " - "routing.\n", - int(netsQueue.size())); + if (ctx->verbose) + log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime " + "revisits).\n", + visitCnt, (100.0 * revisitCnt) / visitCnt, + (100.0 * overtimeRevisitCnt) / visitCnt); + + if (!ripupQueue.empty()) { + if (ctx->verbose || iterCnt == 1) + log_info("failed to route %d nets. re-routing in ripup " + "mode.\n", + int(ripupQueue.size())); + + printNets = ctx->verbose && (ripupQueue.size() < 10); + + visitCnt = 0; + revisitCnt = 0; + overtimeRevisitCnt = 0; + netCnt = 0; + int ripCnt = 0; + + std::vector ripupArray(ripupQueue.begin(), + ripupQueue.end()); + ctx->sorted_shuffle(ripupArray); + + for (auto net_name : ripupArray) { + if (printNets) + log_info(" routing net %s. (%d users)\n", + net_name.c_str(ctx), + int(ctx->nets.at(net_name)->users.size())); + + Router router(ctx, scores, net_name, true, ripup_penalty); + + netCnt++; + visitCnt += router.visitCnt; + revisitCnt += router.revisitCnt; + overtimeRevisitCnt += router.overtimeRevisitCnt; + + if (!router.routedOkay) + log_error("Net %s is impossible to route.\n", + net_name.c_str(ctx)); + + for (auto it : router.rippedNets) + netsQueue.insert(it); + + if (printNets) { + if (router.rippedNets.size() < 10) { + log_info(" ripped up %d other nets:\n", + int(router.rippedNets.size())); + for (auto n : router.rippedNets) + log_info(" %s (%d users)\n", n.c_str(ctx), + int(ctx->nets.at(n)->users.size())); + } else { + log_info(" ripped up %d other nets.\n", + int(router.rippedNets.size())); + } + } + + ripCnt += router.rippedNets.size(); + + if ((ctx->verbose || iterCnt == 1) && !printNets && + (netCnt % 100 == 0)) + log_info(" routed %d nets, ripped %d nets.\n", netCnt, + ripCnt); + } + + if ((ctx->verbose || iterCnt == 1) && (netCnt % 100 != 0)) + log_info(" routed %d nets, ripped %d nets.\n", netCnt, + ripCnt); + + if (ctx->verbose) + log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% " + "overtime revisits).\n", + visitCnt, (100.0 * revisitCnt) / visitCnt, + (100.0 * overtimeRevisitCnt) / visitCnt); + + if (ctx->verbose && !netsQueue.empty()) + log_info(" ripped up %d previously routed nets. continue " + "routing.\n", + int(netsQueue.size())); + } + + if (!ctx->verbose) + log_info( + "iteration %d: routed %d nets without ripup, routed %d " + "nets with ripup.\n", + iterCnt, normalRouteCnt, int(ripupQueue.size())); + + if (iterCnt == 8 || iterCnt == 16 || iterCnt == 32 || + iterCnt == 64 || iterCnt == 128) + ripup_penalty += ctx->getRipupDelayPenalty(); } - } - log_info("routing complete after %d iterations.\n", iterCnt); - return true; + log_info("routing complete after %d iterations.\n", iterCnt); + log_info("Checksum: 0x%08x\n", ctx->checksum()); + return true; + } catch (log_execution_error_exception) { + return false; + } } bool get_actual_route_delay(Context *ctx, WireId src_wire, WireId dst_wire, delay_t &delay) { - Router router(ctx, src_wire, dst_wire); + RipupScoreboard scores; + Router router(ctx, scores, src_wire, dst_wire); if (router.routedOkay) delay = router.visited.at(dst_wire).delay; return router.routedOkay; diff --git a/common/timing.cc b/common/timing.cc index 8da26077..ac116711 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -78,6 +78,7 @@ static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, void assign_budget(Context *ctx, float default_clock) { + log_break(); log_info("Annotating ports with timing budgets\n"); // Clear delays to a very high value first delay_t default_slack = delay_t(1.0e12 / default_clock); @@ -101,7 +102,6 @@ void assign_budget(Context *ctx, float default_clock) } } } - const bool debug = true; // Post-allocation check for (auto net : ctx->nets) { @@ -111,13 +111,15 @@ void assign_budget(Context *ctx, float default_clock) "timing budget of %fns\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx), ctx->getDelayNS(user.budget)); - if (debug) - log_warning("port %s.%s, connected to net '%s', has " - "timing budget of %fns\n", - user.cell->name.c_str(ctx), user.port.c_str(ctx), - net.first.c_str(ctx), ctx->getDelayNS(user.budget)); + if (ctx->verbose) + log_info("port %s.%s, connected to net '%s', has " + "timing budget of %fns\n", + user.cell->name.c_str(ctx), user.port.c_str(ctx), + net.first.c_str(ctx), ctx->getDelayNS(user.budget)); } } + + log_info("Checksum: 0x%08x\n", ctx->checksum()); } NEXTPNR_NAMESPACE_END diff --git a/common/util.h b/common/util.h index 34b2ed02..4c60292b 100644 --- a/common/util.h +++ b/common/util.h @@ -20,6 +20,7 @@ #ifndef UTIL_H #define UTIL_H +#include #include #include "nextpnr.h" @@ -56,6 +57,14 @@ bool bool_or_default(const Container &ct, const KeyType &key, bool def = false) { return bool(int_or_default(ct, key, int(def))); }; + +// Wrap an unordered_map, and allow it to be iterated over sorted by key +template +std::map sorted(const std::unordered_map &orig) +{ + return std::map(orig.begin(), orig.end()); +}; + NEXTPNR_NAMESPACE_END #endif diff --git a/dummy/arch.cc b/dummy/arch.cc index 103eee4d..0ed0f11e 100644 --- a/dummy/arch.cc +++ b/dummy/arch.cc @@ -34,6 +34,8 @@ BelId Arch::getBelByName(IdString name) const { return BelId(); } IdString Arch::getBelName(BelId bel) const { return IdString(); } +uint32_t Arch::getBelChecksum(BelId bel) const { return 0; } + void Arch::bindBel(BelId bel, IdString cell) {} void Arch::unbindBel(BelId bel) {} @@ -75,6 +77,8 @@ WireId Arch::getWireByName(IdString name) const { return WireId(); } IdString Arch::getWireName(WireId wire) const { return IdString(); } +uint32_t Arch::getWireChecksum(WireId wire) const { return 0; } + void Arch::bindWire(WireId wire, IdString net) {} void Arch::unbindWire(WireId wire) {} @@ -98,6 +102,8 @@ PipId Arch::getPipByName(IdString name) const { return PipId(); } IdString Arch::getPipName(PipId pip) const { return IdString(); } +uint32_t Arch::getPipChecksum(PipId wire) const { return 0; } + void Arch::bindPip(PipId pip, IdString net) {} void Arch::unbindPip(PipId pip) {} diff --git a/dummy/arch.h b/dummy/arch.h index 43ab3290..d8068b22 100644 --- a/dummy/arch.h +++ b/dummy/arch.h @@ -87,6 +87,7 @@ struct Arch : BaseCtx BelId getBelByName(IdString name) const; IdString getBelName(BelId bel) const; + uint32_t getBelChecksum(BelId bel) const; void bindBel(BelId bel, IdString cell); void unbindBel(BelId bel); bool checkBelAvail(BelId bel) const; @@ -100,6 +101,7 @@ struct Arch : BaseCtx WireId getWireByName(IdString name) const; IdString getWireName(WireId wire) const; + uint32_t getWireChecksum(WireId wire) const; void bindWire(WireId wire, IdString net); void unbindWire(WireId wire); bool checkWireAvail(WireId wire) const; @@ -108,6 +110,7 @@ struct Arch : BaseCtx PipId getPipByName(IdString name) const; IdString getPipName(PipId pip) const; + uint32_t getPipChecksum(PipId pip) const; void bindPip(PipId pip, IdString net); void unbindPip(PipId pip); bool checkPipAvail(PipId pip) const; @@ -123,7 +126,9 @@ struct Arch : BaseCtx void estimatePosition(BelId bel, int &x, int &y, bool &gb) const; delay_t estimateDelay(WireId src, WireId dst) const; delay_t getDelayEpsilon() const { return 0.01; } + delay_t getRipupDelayPenalty() const { return 1.0; } float getDelayNS(delay_t v) const { return v; } + uint32_t getDelayChecksum(delay_t v) const { return 0; } std::vector getFrameGraphics() const; std::vector getBelGraphics(BelId bel) const; diff --git a/dummy/main.cc b/dummy/main.cc index 6375eef9..110c5b6c 100644 --- a/dummy/main.cc +++ b/dummy/main.cc @@ -20,20 +20,108 @@ #ifdef MAIN_EXECUTABLE #include +#include +#include +#include "log.h" #include "mainwindow.h" #include "nextpnr.h" +#include "pybindings.h" +#include "version.h" USING_NEXTPNR_NAMESPACE int main(int argc, char *argv[]) { - Context ctx(ArchArgs{}); + try { - QApplication a(argc, argv); - MainWindow w(&ctx); - w.show(); + namespace po = boost::program_options; + int rc = 0; - return a.exec(); + 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"); + options.add_options()("gui", "start gui"); + options.add_options()("run", po::value>(), + "python file to execute"); + options.add_options()("version,V", "show version"); + po::positional_options_description pos; + pos.add("run", -1); + + 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; + } + + 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; + } + + Context ctx(ArchArgs{}); + init_python(argv[0]); + python_export_global("ctx", ctx); + + if (vm.count("verbose")) { + ctx.verbose = true; + } + + if (vm.count("force")) { + ctx.force = true; + } + + if (vm.count("seed")) { + ctx.rngseed(vm["seed"].as()); + } + + if (vm.count("run")) { + std::vector files = + vm["run"].as>(); + for (auto filename : files) + execute_python_file(filename.c_str()); + } + + if (vm.count("gui")) { + QApplication a(argc, argv); + MainWindow w(&ctx); + w.show(); + + rc = a.exec(); + } + deinit_python(); + return rc; + } catch (log_execution_error_exception) { +#if defined(_MSC_VER) + _exit(EXIT_FAILURE); +#else + _Exit(EXIT_FAILURE); +#endif + } } #endif diff --git a/frontend/json/jsonparse.cc b/frontend/json/jsonparse.cc index a936bdaa..a832e9e5 100644 --- a/frontend/json/jsonparse.cc +++ b/frontend/json/jsonparse.cc @@ -659,7 +659,8 @@ static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, std::copy(net->attrs.begin(), net->attrs.end(), std::inserter(iobuf->attrs, iobuf->attrs.begin())); if (type == PORT_IN) { - log_info("processing input port %s\n", name.c_str()); + if (ctx->verbose) + log_info("processing input port %s\n", name.c_str()); iobuf->type = ctx->id("$nextpnr_ibuf"); iobuf->ports[ctx->id("O")] = PortInfo{ctx->id("O"), net, PORT_OUT}; // Special case: input, etc, directly drives inout @@ -671,7 +672,8 @@ static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, net->driver.port = ctx->id("O"); net->driver.cell = iobuf; } else if (type == PORT_OUT) { - log_info("processing output port %s\n", name.c_str()); + if (ctx->verbose) + log_info("processing output port %s\n", name.c_str()); iobuf->type = ctx->id("$nextpnr_obuf"); iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), net, PORT_IN}; PortRef ref; @@ -679,7 +681,8 @@ static void insert_iobuf(Context *ctx, NetInfo *net, PortType type, ref.port = ctx->id("I"); net->users.push_back(ref); } else if (type == PORT_INOUT) { - log_info("processing inout port %s\n", name.c_str()); + if (ctx->verbose) + log_info("processing inout port %s\n", name.c_str()); iobuf->type = ctx->id("$nextpnr_iobuf"); iobuf->ports[ctx->id("I")] = PortInfo{ctx->id("I"), nullptr, PORT_IN}; @@ -792,17 +795,14 @@ void json_import(Context *ctx, string modname, JsonNode *node) } check_all_nets_driven(ctx); } +}; // End Namespace JsonParser -struct JsonFrontend +bool parse_json_file(std::istream &f, std::string &filename, Context *ctx) { - // JsonFrontend() : Frontend("json", "read JSON file") { } - JsonFrontend(void) {} - virtual void help() {} - virtual void execute(std::istream *&f, std::string &filename, Context *ctx) - { - // log_header(ctx, "Executing JSON frontend.\n"); + try { + using namespace JsonParser; - JsonNode root(*f); + JsonNode root(f); if (root.type != 'D') log_error("JSON root node is not a dictionary.\n"); @@ -816,15 +816,13 @@ struct JsonFrontend for (auto &it : modules->data_dict) json_import(ctx, it.first, it.second); } + + log_info("Checksum: 0x%08x\n", ctx->checksum()); + log_break(); + return true; + } catch (log_execution_error_exception) { + return false; } -}; // JsonFrontend; - -}; // End Namespace JsonParser - -void parse_json_file(std::istream *&f, std::string &filename, Context *ctx) -{ - auto *parser = new JsonParser::JsonFrontend(); - parser->execute(f, filename, ctx); } NEXTPNR_NAMESPACE_END diff --git a/frontend/json/jsonparse.h b/frontend/json/jsonparse.h index ee3d19ca..fe71444f 100644 --- a/frontend/json/jsonparse.h +++ b/frontend/json/jsonparse.h @@ -26,7 +26,7 @@ NEXTPNR_NAMESPACE_BEGIN -extern void parse_json_file(std::istream *&, std::string &, Context *); +extern bool parse_json_file(std::istream &, std::string &, Context *); NEXTPNR_NAMESPACE_END diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 5db86a9b..f19d2d20 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -2,7 +2,8 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) aux_source_directory(. GUI_SOURCE_FILES) -set(_RESOURCES nextpnr.qrc) +aux_source_directory(${family}/ GUI_SOURCE_FILES) +set(_RESOURCES base.qrc ${family}/nextpnr.qrc) qt5_add_resources(GUI_RESOURCE_FILES ${_RESOURCES}) @@ -10,6 +11,6 @@ set(GUI_LIBRARY_FILES_${ufamily} Qt5::Widgets Qt5::OpenGL ${OPENGL_LIBRARIES} Qt add_library(gui_${family} STATIC ${GUI_SOURCE_FILES} ${GUI_RESOURCE_FILES}) -target_include_directories(gui_${family} PRIVATE ../${family}/) +target_include_directories(gui_${family} PRIVATE ../${family} ${family} ../3rdparty/QtPropertyBrowser/src) target_compile_definitions(gui_${family} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family} QT_NO_KEYWORDS) target_link_libraries(gui_${family} Qt5::Widgets) diff --git a/gui/nextpnr.qrc b/gui/base.qrc similarity index 100% rename from gui/nextpnr.qrc rename to gui/base.qrc diff --git a/gui/mainwindow.cc b/gui/basewindow.cc similarity index 66% rename from gui/mainwindow.cc rename to gui/basewindow.cc index 91232390..f16b205d 100644 --- a/gui/mainwindow.cc +++ b/gui/basewindow.cc @@ -1,39 +1,29 @@ -#include "mainwindow.h" #include #include #include #include -#include -#include #include -#include -#include -#include #include "designwidget.h" #include "fpgaviewwidget.h" #include "jsonparse.h" #include "log.h" +#include "mainwindow.h" #include "pythontab.h" -//#include "pack.h" -//#include "pcf.h" -#include "place_sa.h" -#include "pybindings.h" -#include "route.h" -//#include "bitstream.h" -#include "design_utils.h" -MainWindow::MainWindow(Context *_ctx, QWidget *parent) +static void initBasenameResource() { Q_INIT_RESOURCE(base); } + +NEXTPNR_NAMESPACE_BEGIN + +BaseMainWindow::BaseMainWindow(Context *_ctx, QWidget *parent) : QMainWindow(parent), ctx(_ctx) { - Q_INIT_RESOURCE(nextpnr); + initBasenameResource(); + qRegisterMetaType(); log_files.clear(); log_streams.clear(); - log_write_function = [this](std::string text) { info->info(text); }; - std::string title = "nextpnr-ice40 - " + ctx->getChipName(); - setWindowTitle(title.c_str()); - setObjectName(QStringLiteral("MainWindow")); + setObjectName(QStringLiteral("BaseMainWindow")); resize(1024, 768); createMenusAndBars(); @@ -64,15 +54,19 @@ MainWindow::MainWindow(Context *_ctx, QWidget *parent) tabWidget->addTab(new PythonTab(), "Python"); info = new InfoTab(); tabWidget->addTab(info, "Info"); - splitter_v->addWidget(new FPGAViewWidget()); + + centralTabWidget = new QTabWidget(); + centralTabWidget->addTab(new FPGAViewWidget(), "Graphics"); + + splitter_v->addWidget(centralTabWidget); splitter_v->addWidget(tabWidget); } -MainWindow::~MainWindow() {} +BaseMainWindow::~BaseMainWindow() {} -void MainWindow::writeInfo(std::string text) { info->info(text); } +void BaseMainWindow::writeInfo(std::string text) { info->info(text); } -void MainWindow::createMenusAndBars() +void BaseMainWindow::createMenusAndBars() { QAction *actionOpen = new QAction("Open", this); QIcon icon1; @@ -101,7 +95,7 @@ void MainWindow::createMenusAndBars() QAction *actionAbout = new QAction("About", this); - QMenuBar *menuBar = new QMenuBar(); + menuBar = new QMenuBar(); menuBar->setGeometry(QRect(0, 0, 1024, 27)); QMenu *menu_File = new QMenu("&File", menuBar); QMenu *menu_Help = new QMenu("&Help", menuBar); @@ -109,10 +103,10 @@ void MainWindow::createMenusAndBars() menuBar->addAction(menu_Help->menuAction()); setMenuBar(menuBar); - QToolBar *mainToolBar = new QToolBar(); + mainToolBar = new QToolBar(); addToolBar(Qt::TopToolBarArea, mainToolBar); - QStatusBar *statusBar = new QStatusBar(); + statusBar = new QStatusBar(); setStatusBar(statusBar); menu_File->addAction(actionOpen); @@ -125,21 +119,4 @@ void MainWindow::createMenusAndBars() mainToolBar->addAction(actionSave); } -void MainWindow::open() -{ - QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(), - QString("*.json")); - if (!fileName.isEmpty()) { - tabWidget->setCurrentWidget(info); - - std::string fn = fileName.toStdString(); - std::istream *f = new std::ifstream(fn); - - parse_json_file(f, fn, ctx); - - // pack_design(ctx); - print_utilisation(ctx); - } -} - -bool MainWindow::save() { return false; } +NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.h b/gui/basewindow.h new file mode 100644 index 00000000..55e4affc --- /dev/null +++ b/gui/basewindow.h @@ -0,0 +1,49 @@ +#ifndef BASEMAINWINDOW_H +#define BASEMAINWINDOW_H + +#include "infotab.h" +#include "nextpnr.h" + +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(std::string) + +NEXTPNR_NAMESPACE_BEGIN + +class BaseMainWindow : public QMainWindow +{ + Q_OBJECT + + public: + explicit BaseMainWindow(Context *ctx, QWidget *parent = 0); + virtual ~BaseMainWindow(); + Context *getContext() { return ctx; } + + protected: + void createMenusAndBars(); + + protected Q_SLOTS: + void writeInfo(std::string text); + + virtual void open() = 0; + virtual bool save() = 0; + + protected: + Context *ctx; + QTabWidget *tabWidget; + QTabWidget *centralTabWidget; + InfoTab *info; + + QMenuBar *menuBar; + QToolBar *mainToolBar; + QStatusBar *statusBar; +}; + +NEXTPNR_NAMESPACE_END + +#endif // BASEMAINWINDOW_H diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 40e69403..7532653d 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -7,6 +7,8 @@ #include "fpgaviewwidget.h" #include "pybindings.h" +NEXTPNR_NAMESPACE_BEGIN + enum class ElementType { BEL, @@ -234,3 +236,5 @@ void DesignWidget::selectObject() { Q_EMIT info("selected " + itemContextMenu->text(0).toStdString() + "\n"); } + +NEXTPNR_NAMESPACE_END diff --git a/gui/designwidget.h b/gui/designwidget.h index 9682726c..5bd12d4d 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -7,8 +7,7 @@ #include "qttreepropertybrowser.h" #include "qtvariantproperty.h" -// FIXME -USING_NEXTPNR_NAMESPACE +NEXTPNR_NAMESPACE_BEGIN class DesignWidget : public QWidget { @@ -45,4 +44,6 @@ class DesignWidget : public QWidget QMap idToProperty; }; +NEXTPNR_NAMESPACE_END + #endif // DESIGNWIDGET_H diff --git a/gui/dummy/mainwindow.cc b/gui/dummy/mainwindow.cc new file mode 100644 index 00000000..da162dd0 --- /dev/null +++ b/gui/dummy/mainwindow.cc @@ -0,0 +1,30 @@ +#include "mainwindow.h" + +static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } + +NEXTPNR_NAMESPACE_BEGIN + +MainWindow::MainWindow(Context *_ctx, QWidget *parent) + : BaseMainWindow(_ctx, parent) +{ + initMainResource(); + + std::string title = "nextpnr-dummy - " + ctx->getChipName(); + setWindowTitle(title.c_str()); + + createMenu(); +} + +MainWindow::~MainWindow() {} + +void MainWindow::createMenu() +{ + QMenu *menu_Custom = new QMenu("&Dummy", menuBar); + menuBar->addAction(menu_Custom->menuAction()); +} + +void MainWindow::open() {} + +bool MainWindow::save() { return false; } + +NEXTPNR_NAMESPACE_END diff --git a/gui/dummy/mainwindow.h b/gui/dummy/mainwindow.h new file mode 100644 index 00000000..c2786906 --- /dev/null +++ b/gui/dummy/mainwindow.h @@ -0,0 +1,26 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "../basewindow.h" + +NEXTPNR_NAMESPACE_BEGIN + +class MainWindow : public BaseMainWindow +{ + Q_OBJECT + + public: + explicit MainWindow(Context *ctx, QWidget *parent = 0); + virtual ~MainWindow(); + + public: + void createMenu(); + + protected Q_SLOTS: + virtual void open(); + virtual bool save(); +}; + +NEXTPNR_NAMESPACE_END + +#endif // MAINWINDOW_H diff --git a/gui/dummy/nextpnr.qrc b/gui/dummy/nextpnr.qrc new file mode 100644 index 00000000..03585ec0 --- /dev/null +++ b/gui/dummy/nextpnr.qrc @@ -0,0 +1,2 @@ + + diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 4e60c56b..5dbc8952 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -217,14 +217,14 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) : QOpenGLWidget(parent), moveX_(0), moveY_(0), zoom_(10.0f), lineShader_(this) { - ctx = qobject_cast(getMainWindow())->getContext(); + ctx = qobject_cast(getMainWindow())->getContext(); } QMainWindow *FPGAViewWidget::getMainWindow() { QWidgetList widgets = qApp->topLevelWidgets(); for (QWidgetList::iterator i = widgets.begin(); i != widgets.end(); ++i) - if ((*i)->objectName() == "MainWindow") + if ((*i)->objectName() == "BaseMainWindow") return (QMainWindow *)(*i); return NULL; } diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc new file mode 100644 index 00000000..4c7bc18f --- /dev/null +++ b/gui/ice40/mainwindow.cc @@ -0,0 +1,82 @@ +#include "mainwindow.h" +#include +#include +#include +#include "bitstream.h" +#include "design_utils.h" +#include "jsonparse.h" +#include "log.h" +#include "pack.h" +#include "pcf.h" +#include "place_sa.h" +#include "route.h" + +static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } + +NEXTPNR_NAMESPACE_BEGIN + +MainWindow::MainWindow(Context *_ctx, QWidget *parent) + : BaseMainWindow(_ctx, parent) +{ + initMainResource(); + + std::string title = "nextpnr-ice40 - " + ctx->getChipName(); + setWindowTitle(title.c_str()); + + task = new TaskManager(_ctx); + connect(task, SIGNAL(log(std::string)), this, SLOT(writeInfo(std::string))); + + createMenu(); +} + +MainWindow::~MainWindow() { delete task; } + +void MainWindow::createMenu() +{ + QMenu *menu_Custom = new QMenu("&ICE 40", menuBar); + menuBar->addAction(menu_Custom->menuAction()); + + QAction *actionPlay = new QAction("Play", this); + QIcon icon1; + icon1.addFile(QStringLiteral(":/icons/resources/control_play.png")); + actionPlay->setIcon(icon1); + actionPlay->setStatusTip("Continue running task"); + connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread())); + + QAction *actionPause = new QAction("Pause", this); + QIcon icon2; + icon2.addFile(QStringLiteral(":/icons/resources/control_pause.png")); + actionPause->setIcon(icon2); + actionPause->setStatusTip("Pause running task"); + connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread())); + + QAction *actionStop = new QAction("Stop", this); + QIcon icon3; + icon3.addFile(QStringLiteral(":/icons/resources/control_stop.png")); + actionStop->setIcon(icon3); + actionStop->setStatusTip("Stop running task"); + connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread())); + + QToolBar *taskToolBar = new QToolBar(); + addToolBar(Qt::TopToolBarArea, taskToolBar); + + taskToolBar->addAction(actionPlay); + taskToolBar->addAction(actionPause); + taskToolBar->addAction(actionStop); +} + +void MainWindow::open() +{ + QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(), + QString("*.json")); + if (!fileName.isEmpty()) { + tabWidget->setCurrentWidget(info); + + std::string fn = fileName.toStdString(); + Q_EMIT task->parsejson(fn); + } +} + +bool MainWindow::save() { return false; } + +NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h new file mode 100644 index 00000000..712f341a --- /dev/null +++ b/gui/ice40/mainwindow.h @@ -0,0 +1,30 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "../basewindow.h" +#include "worker.h" + +NEXTPNR_NAMESPACE_BEGIN + +class MainWindow : public BaseMainWindow +{ + Q_OBJECT + + public: + explicit MainWindow(Context *ctx, QWidget *parent = 0); + virtual ~MainWindow(); + + public: + void createMenu(); + + protected Q_SLOTS: + virtual void open(); + virtual bool save(); + + private: + TaskManager *task; +}; + +NEXTPNR_NAMESPACE_END + +#endif // MAINWINDOW_H diff --git a/gui/ice40/nextpnr.qrc b/gui/ice40/nextpnr.qrc new file mode 100644 index 00000000..cbdb8b26 --- /dev/null +++ b/gui/ice40/nextpnr.qrc @@ -0,0 +1,7 @@ + + + resources/control_play.png + resources/control_pause.png + resources/control_stop.png + + diff --git a/gui/ice40/resources/control_pause.png b/gui/ice40/resources/control_pause.png new file mode 100644 index 00000000..2d9ce9c4 Binary files /dev/null and b/gui/ice40/resources/control_pause.png differ diff --git a/gui/ice40/resources/control_play.png b/gui/ice40/resources/control_play.png new file mode 100644 index 00000000..0846555d Binary files /dev/null and b/gui/ice40/resources/control_play.png differ diff --git a/gui/ice40/resources/control_stop.png b/gui/ice40/resources/control_stop.png new file mode 100644 index 00000000..893bb60e Binary files /dev/null and b/gui/ice40/resources/control_stop.png differ diff --git a/gui/ice40/worker.cc b/gui/ice40/worker.cc new file mode 100644 index 00000000..9549f659 --- /dev/null +++ b/gui/ice40/worker.cc @@ -0,0 +1,112 @@ +#include "worker.h" +#include +#include "bitstream.h" +#include "design_utils.h" +#include "jsonparse.h" +#include "log.h" +#include "pack.h" +#include "pcf.h" +#include "place_sa.h" +#include "route.h" +#include "timing.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct WorkerInterruptionRequested +{ +}; + +Worker::Worker(Context *_ctx, TaskManager *parent) : ctx(_ctx) +{ + log_write_function = [this, parent](std::string text) { + Q_EMIT log(text); + if (parent->shouldTerminate()) { + parent->clearTerminate(); + throw WorkerInterruptionRequested(); + } + while (parent->isPaused()) { + QThread::sleep(1); + } + }; +} + +void Worker::parsejson(const std::string &filename) +{ + std::string fn = filename; + std::ifstream f(fn); + try { + if (!parse_json_file(f, fn, ctx)) + log_error("Loading design failed.\n"); + if (!pack_design(ctx)) + log_error("Packing design failed.\n"); + double freq = 50e6; + assign_budget(ctx, freq); + print_utilisation(ctx); + + if (!place_design_sa(ctx)) + log_error("Placing design failed.\n"); + if (!route_design(ctx)) + log_error("Routing design failed.\n"); + Q_EMIT log("DONE\n"); + } catch (log_execution_error_exception) { + } catch (WorkerInterruptionRequested) { + Q_EMIT log("CANCELED\n"); + } +} + +TaskManager::TaskManager(Context *ctx) : toTerminate(false), toPause(false) +{ + Worker *worker = new Worker(ctx, this); + worker->moveToThread(&workerThread); + connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(this, &TaskManager::parsejson, worker, &Worker::parsejson); + connect(worker, &Worker::log, this, &TaskManager::info); + workerThread.start(); +} + +TaskManager::~TaskManager() +{ + if (workerThread.isRunning()) + terminate_thread(); + workerThread.quit(); + workerThread.wait(); +} + +void TaskManager::info(const std::string &result) { Q_EMIT log(result); } + +void TaskManager::terminate_thread() +{ + QMutexLocker locker(&mutex); + toTerminate = true; +} + +bool TaskManager::shouldTerminate() +{ + QMutexLocker locker(&mutex); + return toTerminate; +} + +void TaskManager::clearTerminate() +{ + QMutexLocker locker(&mutex); + toTerminate = false; +} + +void TaskManager::pause_thread() +{ + QMutexLocker locker(&mutex); + toPause = true; +} + +void TaskManager::continue_thread() +{ + QMutexLocker locker(&mutex); + toPause = false; +} +bool TaskManager::isPaused() +{ + QMutexLocker locker(&mutex); + return toPause; +} + +NEXTPNR_NAMESPACE_END diff --git a/gui/ice40/worker.h b/gui/ice40/worker.h new file mode 100644 index 00000000..181fafa3 --- /dev/null +++ b/gui/ice40/worker.h @@ -0,0 +1,55 @@ +#ifndef WORKER_H +#define WORKER_H + +#include +#include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +class TaskManager; + +class Worker : public QObject +{ + Q_OBJECT + public: + Worker(Context *ctx, TaskManager *parent); + public Q_SLOTS: + void parsejson(const std::string &filename); + Q_SIGNALS: + void log(const std::string &text); + + private: + Context *ctx; +}; + +class TaskManager : public QObject +{ + Q_OBJECT + QThread workerThread; + + public: + TaskManager(Context *ctx); + ~TaskManager(); + bool shouldTerminate(); + void clearTerminate(); + bool isPaused(); + public Q_SLOTS: + void info(const std::string &text); + void terminate_thread(); + void pause_thread(); + void continue_thread(); + Q_SIGNALS: + void terminate(); + void parsejson(const std::string &); + void log(const std::string &text); + + private: + QMutex mutex; + bool toTerminate; + bool toPause; +}; + +NEXTPNR_NAMESPACE_END + +#endif // WORKER_H diff --git a/gui/infotab.cc b/gui/infotab.cc index 7690b83c..29d557d2 100644 --- a/gui/infotab.cc +++ b/gui/infotab.cc @@ -1,6 +1,8 @@ #include "infotab.h" #include +NEXTPNR_NAMESPACE_BEGIN + InfoTab::InfoTab(QWidget *parent) : QWidget(parent) { plainTextEdit = new QPlainTextEdit(); @@ -37,3 +39,5 @@ void InfoTab::showContextMenu(const QPoint &pt) } void InfoTab::clearBuffer() { plainTextEdit->clear(); } + +NEXTPNR_NAMESPACE_END diff --git a/gui/infotab.h b/gui/infotab.h index d7f1408c..3e0220c4 100644 --- a/gui/infotab.h +++ b/gui/infotab.h @@ -5,8 +5,7 @@ #include #include "nextpnr.h" -// FIXME -USING_NEXTPNR_NAMESPACE +NEXTPNR_NAMESPACE_BEGIN class InfoTab : public QWidget { @@ -24,4 +23,6 @@ class InfoTab : public QWidget QMenu *contextMenu; }; +NEXTPNR_NAMESPACE_END + #endif // INFOTAB_H diff --git a/gui/line_editor.cc b/gui/line_editor.cc index b5ed955f..6299c9cc 100644 --- a/gui/line_editor.cc +++ b/gui/line_editor.cc @@ -1,7 +1,8 @@ #include "line_editor.h" - #include +NEXTPNR_NAMESPACE_BEGIN + LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0) { setContextMenuPolicy(Qt::CustomContextMenu); @@ -64,4 +65,6 @@ void LineEditor::clearHistory() lines.clear(); index = 0; clear(); -} \ No newline at end of file +} + +NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/gui/line_editor.h b/gui/line_editor.h index 15b675f9..5f27e502 100644 --- a/gui/line_editor.h +++ b/gui/line_editor.h @@ -3,6 +3,9 @@ #include #include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN class LineEditor : public QLineEdit { @@ -28,4 +31,6 @@ class LineEditor : public QLineEdit QMenu *contextMenu; }; +NEXTPNR_NAMESPACE_END + #endif // LINE_EDITOR_H diff --git a/gui/mainwindow.h b/gui/mainwindow.h deleted file mode 100644 index 35d917d9..00000000 --- a/gui/mainwindow.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include "infotab.h" -#include "nextpnr.h" - -#include -#include - -// FIXME -USING_NEXTPNR_NAMESPACE - -class MainWindow : public QMainWindow -{ - Q_OBJECT - - public: - explicit MainWindow(Context *ctx, QWidget *parent = 0); - ~MainWindow(); - Context *getContext() { return ctx; } - - private: - void createMenusAndBars(); - - private Q_SLOTS: - void writeInfo(std::string text); - void open(); - bool save(); - - private: - Context *ctx; - QTabWidget *tabWidget; - InfoTab *info; -}; - -#endif // MAINWINDOW_H diff --git a/gui/pythontab.cc b/gui/pythontab.cc index 96a6c4b9..19aa0162 100644 --- a/gui/pythontab.cc +++ b/gui/pythontab.cc @@ -3,6 +3,8 @@ #include "emb.h" #include "pybindings.h" +NEXTPNR_NAMESPACE_BEGIN + PythonTab::PythonTab(QWidget *parent) : QWidget(parent) { PyImport_ImportModule("emb"); @@ -114,4 +116,6 @@ void PythonTab::showContextMenu(const QPoint &pt) contextMenu->exec(mapToGlobal(pt)); } -void PythonTab::clearBuffer() { plainTextEdit->clear(); } \ No newline at end of file +void PythonTab::clearBuffer() { plainTextEdit->clear(); } + +NEXTPNR_NAMESPACE_END diff --git a/gui/pythontab.h b/gui/pythontab.h index 5aed8b0b..52a8ff8d 100644 --- a/gui/pythontab.h +++ b/gui/pythontab.h @@ -8,8 +8,7 @@ #include "line_editor.h" #include "nextpnr.h" -// FIXME -USING_NEXTPNR_NAMESPACE +NEXTPNR_NAMESPACE_BEGIN class PythonTab : public QWidget { @@ -33,4 +32,6 @@ class PythonTab : public QWidget emb::stdout_write_type write; }; +NEXTPNR_NAMESPACE_END + #endif // PYTHONTAB_H diff --git a/ice40/arch.h b/ice40/arch.h index 85fb9fc0..f3a46f5c 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -515,6 +515,8 @@ struct Arch : BaseCtx return id(chip_info->bel_data[bel.index].name.get()); } + uint32_t getBelChecksum(BelId bel) const { return bel.index; } + void bindBel(BelId bel, IdString cell) { assert(bel != BelId()); @@ -607,6 +609,8 @@ struct Arch : BaseCtx return id(chip_info->wire_data[wire.index].name.get()); } + uint32_t getWireChecksum(WireId wire) const { return wire.index; } + void bindWire(WireId wire, IdString net) { assert(wire != WireId()); @@ -646,6 +650,8 @@ struct Arch : BaseCtx PipId getPipByName(IdString name) const; IdString getPipName(PipId pip) const; + uint32_t getPipChecksum(PipId pip) const { return pip.index; } + void bindPip(PipId pip, IdString net) { assert(pip != PipId()); @@ -755,8 +761,10 @@ struct Arch : BaseCtx void estimatePosition(BelId bel, int &x, int &y, bool &gb) const; delay_t estimateDelay(WireId src, WireId dst) const; - delay_t getDelayEpsilon() const { return 10; } + delay_t getDelayEpsilon() const { return 20; } + 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; } // ------------------------------------------------- diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 8754fef7..e722cea4 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -229,6 +229,16 @@ void write_asc(const Context *ctx, std::ostream &out) set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup); } + + if (ctx->args.type == ArchArgs::UP5K) { + if (iez == 0) { + set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", + !pullup); + } else if (iez == 1) { + set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", + !pullup); + } + } } else if (cell.second->type == ctx->id("SB_GB")) { // no cell config bits } else if (cell.second->type == ctx->id("ICESTORM_RAM")) { @@ -312,7 +322,8 @@ void write_asc(const Context *ctx, std::ostream &out) ctx->args.type == ArchArgs::HX8K) { setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25); } else if (ctx->args.type == ArchArgs::UP5K) { - if (tile == TILE_LOGIC) { + if (tile == TILE_LOGIC || tile == TILE_RAMB || + tile == TILE_RAMT) { setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27); } else { diff --git a/ice40/main.cc b/ice40/main.cc index c82cada3..f2100d45 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -39,6 +39,8 @@ #include "timing.h" #include "version.h" +USING_NEXTPNR_NAMESPACE + void svg_dump_el(const GraphicElement &el) { float scale = 10.0, offset = 10.0; @@ -61,268 +63,291 @@ void svg_dump_el(const GraphicElement &el) int main(int argc, char *argv[]) { - namespace po = boost::program_options; - 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()("force,f", "keep running after errors"); - options.add_options()("gui", "start gui"); - options.add_options()("svg", "dump SVG file"); - options.add_options()("pack-only", - "pack design only without placement or routing"); - - options.add_options()("run", po::value>(), - "python file to execute"); - 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()("seed", po::value(), - "seed value for random number generator"); - options.add_options()("version,V", "show version"); - options.add_options()("tmfuzz", "run path delay estimate fuzzer"); - 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"); - options.add_options()("package", po::value(), - "set device package"); - po::positional_options_description pos; - pos.add("run", -1); - - po::variables_map vm; try { - po::parsed_options parsed = po::command_line_parser(argc, argv) - .options(options) - .positional(pos) - .run(); + namespace po = boost::program_options; + int rc = 0; + std::string str; - po::store(parsed, vm); + log_files.push_back(stdout); - po::notify(vm); - } + 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"); + options.add_options()("gui", "start gui"); + options.add_options()("svg", "dump SVG file"); + options.add_options()("pack-only", + "pack design only without placement or routing"); - catch (std::exception &e) { - std::cout << e.what() << "\n"; - return 1; - } + options.add_options()("run", po::value>(), + "python file to execute"); + 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()("seed", po::value(), + "seed value for random number generator"); + options.add_options()("version,V", "show version"); + options.add_options()("tmfuzz", "run path delay estimate fuzzer"); + 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"); + options.add_options()("freq", po::value(), + "set target frequency for design in MHz"); + options.add_options()("package", po::value(), + "set device package"); + po::positional_options_description pos; + pos.add("run", -1); - 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; - } + po::variables_map vm; + try { + po::parsed_options parsed = po::command_line_parser(argc, argv) + .options(options) + .positional(pos) + .run(); - 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; - } + po::store(parsed, vm); - ArchArgs chipArgs; + po::notify(vm); + } - if (vm.count("lp384")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::LP384; - chipArgs.package = "qn32"; - } + catch (std::exception &e) { + std::cout << e.what() << "\n"; + return 1; + } - if (vm.count("lp1k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::LP1K; - chipArgs.package = "tq144"; - } + 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("lp8k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::LP8K; - chipArgs.package = "ct256"; - } + 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("hx1k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::HX1K; - chipArgs.package = "tq144"; - } + ArchArgs chipArgs; - if (vm.count("hx8k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::HX8K; - chipArgs.package = "ct256"; - } + if (vm.count("lp384")) { + if (chipArgs.type != ArchArgs::NONE) + goto help; + chipArgs.type = ArchArgs::LP384; + chipArgs.package = "qn32"; + } - if (vm.count("up5k")) { - if (chipArgs.type != ArchArgs::NONE) - goto help; - chipArgs.type = ArchArgs::UP5K; - chipArgs.package = "sg48"; - } + if (vm.count("lp1k")) { + if (chipArgs.type != ArchArgs::NONE) + goto help; + chipArgs.type = ArchArgs::LP1K; + chipArgs.package = "tq144"; + } - if (chipArgs.type == ArchArgs::NONE) { - chipArgs.type = ArchArgs::HX1K; - chipArgs.package = "tq144"; - } + if (vm.count("lp8k")) { + if (chipArgs.type != ArchArgs::NONE) + goto help; + 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("hx8k")) { + if (chipArgs.type != ArchArgs::NONE) + goto help; + 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 (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; - } + 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("package")) + chipArgs.package = vm["package"].as(); - Context ctx(chipArgs); - init_python(argv[0]); - python_export_global("ctx", ctx); + Context ctx(chipArgs); + init_python(argv[0]); + python_export_global("ctx", ctx); - if (vm.count("verbose")) { - ctx.verbose = true; - } + if (vm.count("verbose")) { + ctx.verbose = true; + } - if (vm.count("force")) { - ctx.force = true; - } + if (vm.count("debug")) { + ctx.verbose = true; + ctx.debug = true; + } - if (vm.count("seed")) { - ctx.rngseed(vm["seed"].as()); - } + if (vm.count("force")) { + ctx.force = true; + } - if (vm.count("svg")) { - std::cout << "\n"; - for (auto bel : ctx.getBels()) { - std::cout << "\n"; - for (auto &el : ctx.getBelGraphics(bel)) + if (vm.count("seed")) { + ctx.rngseed(vm["seed"].as()); + } + + if (vm.count("svg")) { + std::cout << "\n"; + for (auto bel : ctx.getBels()) { + std::cout << "\n"; + for (auto &el : ctx.getBelGraphics(bel)) + svg_dump_el(el); + } + std::cout << "\n"; + for (auto &el : ctx.getFrameGraphics()) svg_dump_el(el); + std::cout << "\n"; } - std::cout << "\n"; - for (auto &el : ctx.getFrameGraphics()) - svg_dump_el(el); - std::cout << "\n"; - } - if (vm.count("tmfuzz")) { - std::vector src_wires, dst_wires; + if (vm.count("tmfuzz")) { + std::vector src_wires, dst_wires; - /*for (auto w : ctx.getWires()) - src_wires.push_back(w);*/ - for (auto b : ctx.getBels()) { - if (ctx.getBelType(b) == TYPE_ICESTORM_LC) { - src_wires.push_back(ctx.getWireBelPin(b, PIN_O)); + /*for (auto w : ctx.getWires()) + src_wires.push_back(w);*/ + for (auto b : ctx.getBels()) { + if (ctx.getBelType(b) == TYPE_ICESTORM_LC) { + src_wires.push_back(ctx.getWireBelPin(b, PIN_O)); + } + if (ctx.getBelType(b) == TYPE_SB_IO) { + src_wires.push_back(ctx.getWireBelPin(b, PIN_D_IN_0)); + } } - if (ctx.getBelType(b) == TYPE_SB_IO) { - src_wires.push_back(ctx.getWireBelPin(b, PIN_D_IN_0)); + + for (auto b : ctx.getBels()) { + if (ctx.getBelType(b) == TYPE_ICESTORM_LC) { + dst_wires.push_back(ctx.getWireBelPin(b, PIN_I0)); + dst_wires.push_back(ctx.getWireBelPin(b, PIN_I1)); + dst_wires.push_back(ctx.getWireBelPin(b, PIN_I2)); + dst_wires.push_back(ctx.getWireBelPin(b, PIN_I3)); + dst_wires.push_back(ctx.getWireBelPin(b, PIN_CEN)); + dst_wires.push_back(ctx.getWireBelPin(b, PIN_CIN)); + } + if (ctx.getBelType(b) == TYPE_SB_IO) { + dst_wires.push_back(ctx.getWireBelPin(b, PIN_D_OUT_0)); + dst_wires.push_back( + ctx.getWireBelPin(b, PIN_OUTPUT_ENABLE)); + } + } + + ctx.shuffle(src_wires); + ctx.shuffle(dst_wires); + + for (int i = 0; + i < int(src_wires.size()) && i < int(dst_wires.size()); i++) { + delay_t actual_delay; + WireId src = src_wires[i], dst = dst_wires[i]; + if (!get_actual_route_delay(&ctx, src, dst, actual_delay)) + continue; + printf("%s %s %.3f %.3f %d %d %d %d %d %d\n", + ctx.getWireName(src).c_str(&ctx), + ctx.getWireName(dst).c_str(&ctx), + ctx.getDelayNS(actual_delay), + ctx.getDelayNS(ctx.estimateDelay(src, dst)), + ctx.chip_info->wire_data[src.index].x, + ctx.chip_info->wire_data[src.index].y, + ctx.chip_info->wire_data[src.index].type, + ctx.chip_info->wire_data[dst.index].x, + ctx.chip_info->wire_data[dst.index].y, + ctx.chip_info->wire_data[dst.index].type); } } - for (auto b : ctx.getBels()) { - if (ctx.getBelType(b) == TYPE_ICESTORM_LC) { - dst_wires.push_back(ctx.getWireBelPin(b, PIN_I0)); - dst_wires.push_back(ctx.getWireBelPin(b, PIN_I1)); - dst_wires.push_back(ctx.getWireBelPin(b, PIN_I2)); - dst_wires.push_back(ctx.getWireBelPin(b, PIN_I3)); - dst_wires.push_back(ctx.getWireBelPin(b, PIN_CEN)); - dst_wires.push_back(ctx.getWireBelPin(b, PIN_CIN)); + if (vm.count("json")) { + std::string filename = vm["json"].as(); + std::ifstream f(filename); + if (!parse_json_file(f, filename, &ctx)) + log_error("Loading design failed.\n"); + + if (vm.count("pcf")) { + std::ifstream pcf(vm["pcf"].as()); + if (!apply_pcf(&ctx, pcf)) + log_error("Loading PCF failed.\n"); } - if (ctx.getBelType(b) == TYPE_SB_IO) { - dst_wires.push_back(ctx.getWireBelPin(b, PIN_D_OUT_0)); - dst_wires.push_back(ctx.getWireBelPin(b, PIN_OUTPUT_ENABLE)); + + if (!pack_design(&ctx) && !ctx.force) + log_error("Packing design failed.\n"); + double freq = 50e6; + if (vm.count("freq")) + freq = vm["freq"].as() * 1e6; + assign_budget(&ctx, freq); + print_utilisation(&ctx); + + if (!vm.count("pack-only")) { + if (!place_design_sa(&ctx) && !ctx.force) + log_error("Placing design failed.\n"); + if (!route_design(&ctx) && !ctx.force) + log_error("Routing design failed.\n"); } } - ctx.shuffle(src_wires); - ctx.shuffle(dst_wires); - - for (int i = 0; i < int(src_wires.size()) && i < int(dst_wires.size()); - i++) { - delay_t actual_delay; - WireId src = src_wires[i], dst = dst_wires[i]; - if (!get_actual_route_delay(&ctx, src, dst, actual_delay)) - continue; - printf("%s %s %.3f %.3f %d %d %d %d %d %d\n", - ctx.getWireName(src).c_str(&ctx), - ctx.getWireName(dst).c_str(&ctx), - ctx.getDelayNS(actual_delay), - ctx.getDelayNS(ctx.estimateDelay(src, dst)), - ctx.chip_info->wire_data[src.index].x, - ctx.chip_info->wire_data[src.index].y, - ctx.chip_info->wire_data[src.index].type, - ctx.chip_info->wire_data[dst.index].x, - ctx.chip_info->wire_data[dst.index].y, - ctx.chip_info->wire_data[dst.index].type); - } - } - - if (vm.count("json")) { - std::string filename = vm["json"].as(); - std::istream *f = new std::ifstream(filename); - - parse_json_file(f, filename, &ctx); - - if (vm.count("pcf")) { - std::ifstream pcf(vm["pcf"].as()); - apply_pcf(&ctx, pcf); + if (vm.count("asc")) { + std::string filename = vm["asc"].as(); + std::ofstream f(filename); + write_asc(&ctx, f); } - if (!pack_design(&ctx) && !ctx.force) - log_error("Packing design failed.\n"); - assign_budget(&ctx, 50e6); - print_utilisation(&ctx); - - if (!vm.count("pack-only")) { - if (!place_design_sa(&ctx) && !ctx.force) - log_error("Placing design failed.\n"); - if (!route_design(&ctx) && !ctx.force) - log_error("Routing design failed.\n"); + if (vm.count("run")) { + std::vector files = + vm["run"].as>(); + for (auto filename : files) + execute_python_file(filename.c_str()); } - } - if (vm.count("asc")) { - std::string filename = vm["asc"].as(); - std::ofstream f(filename); - write_asc(&ctx, f); - } + if (vm.count("gui")) { + QApplication a(argc, argv); + QSurfaceFormat fmt; + fmt.setSamples(10); + QSurfaceFormat::setDefaultFormat(fmt); + MainWindow w(&ctx); + w.show(); - if (vm.count("run")) { - std::vector files = - vm["run"].as>(); - for (auto filename : files) - execute_python_file(filename.c_str()); + rc = a.exec(); + } + deinit_python(); + return rc; + } catch (log_execution_error_exception) { +#if defined(_MSC_VER) + _exit(EXIT_FAILURE); +#else + _Exit(EXIT_FAILURE); +#endif } - - if (vm.count("gui")) { - QApplication a(argc, argv); - QSurfaceFormat fmt; - fmt.setSamples(10); - QSurfaceFormat::setDefaultFormat(fmt); - MainWindow w(&ctx); - w.show(); - - rc = a.exec(); - } - deinit_python(); - return rc; } #endif diff --git a/ice40/pack.cc b/ice40/pack.cc index 19f1e7bc..35cef8b8 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -24,6 +24,7 @@ #include "cells.h" #include "design_utils.h" #include "log.h" +#include "util.h" NEXTPNR_NAMESPACE_BEGIN @@ -34,10 +35,11 @@ static void pack_lut_lutffs(Context *ctx) std::unordered_set packed_cells; std::vector new_cells; - for (auto cell : ctx->cells) { + for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; - log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), - ci->type.c_str(ctx)); + if (ctx->verbose) + log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), + ci->type.c_str(ctx)); if (is_lut(ctx, ci)) { CellInfo *packed = create_ice_cell(ctx, "ICESTORM_LC", ci->name.str(ctx) + "_LC"); @@ -45,8 +47,9 @@ static void pack_lut_lutffs(Context *ctx) std::inserter(packed->attrs, packed->attrs.begin())); packed_cells.insert(ci->name); new_cells.push_back(packed); - log_info("packed cell %s into %s\n", ci->name.c_str(ctx), - packed->name.c_str(ctx)); + if (ctx->verbose) + log_info("packed cell %s into %s\n", ci->name.c_str(ctx), + packed->name.c_str(ctx)); // See if we can pack into a DFF // TODO: LUT cascade NetInfo *o = ci->ports.at(ctx->id("O")).net; @@ -54,7 +57,8 @@ static void pack_lut_lutffs(Context *ctx) auto lut_bel = ci->attrs.find(ctx->id("BEL")); bool packed_dff = false; if (dff) { - log_info("found attached dff %s\n", dff->name.c_str(ctx)); + if (ctx->verbose) + log_info("found attached dff %s\n", dff->name.c_str(ctx)); auto dff_bel = dff->attrs.find(ctx->id("BEL")); if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) { @@ -66,8 +70,9 @@ static void pack_lut_lutffs(Context *ctx) if (dff_bel != dff->attrs.end()) packed->attrs[ctx->id("BEL")] = dff_bel->second; packed_cells.insert(dff->name); - log_info("packed cell %s into %s\n", dff->name.c_str(ctx), - packed->name.c_str(ctx)); + if (ctx->verbose) + log_info("packed cell %s into %s\n", + dff->name.c_str(ctx), packed->name.c_str(ctx)); packed_dff = true; } } @@ -92,15 +97,16 @@ static void pack_nonlut_ffs(Context *ctx) std::unordered_set packed_cells; std::vector new_cells; - for (auto cell : ctx->cells) { + for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_ff(ctx, ci)) { CellInfo *packed = create_ice_cell(ctx, "ICESTORM_LC", ci->name.str(ctx) + "_DFFLC"); std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin())); - log_info("packed cell %s into %s\n", ci->name.c_str(ctx), - packed->name.c_str(ctx)); + if (ctx->verbose) + log_info("packed cell %s into %s\n", ci->name.c_str(ctx), + packed->name.c_str(ctx)); packed_cells.insert(ci->name); new_cells.push_back(packed); dff_to_lc(ctx, ci, packed, true); @@ -121,7 +127,7 @@ static void pack_carries(Context *ctx) std::unordered_set packed_cells; - for (auto cell : ctx->cells) { + for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_carry(ctx, ci)) { packed_cells.insert(cell.first); @@ -196,7 +202,7 @@ static void pack_ram(Context *ctx) std::unordered_set packed_cells; std::vector new_cells; - for (auto cell : ctx->cells) { + for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_ram(ctx, ci)) { CellInfo *packed = create_ice_cell(ctx, "ICESTORM_RAM", @@ -242,10 +248,11 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, for (auto user : orig->users) { if (user.cell != nullptr) { CellInfo *uc = user.cell; - log_info("%s user %s\n", orig->name.c_str(ctx), - uc->name.c_str(ctx)); - if (is_lut(ctx, uc) && (user.port.str(ctx).at(0) == 'I') && - !constval) { + if (ctx->verbose) + log_info("%s user %s\n", orig->name.c_str(ctx), + uc->name.c_str(ctx)); + if ((is_lut(ctx, uc) || is_lc(ctx, uc)) && + (user.port.str(ctx).at(0) == 'I') && !constval) { uc->ports[user.port].net = nullptr; } else { uc->ports[user.port].net = constnet; @@ -279,7 +286,7 @@ static void pack_constants(Context *ctx) bool gnd_used = false, vcc_used = false; - for (auto net : ctx->nets) { + for (auto net : sorted(ctx->nets)) { NetInfo *ni = net.second; if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) { @@ -323,7 +330,7 @@ static void pack_io(Context *ctx) log_info("Packing IOs..\n"); - for (auto cell : ctx->cells) { + for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_nextpnr_iob(ctx, ci)) { CellInfo *sb = nullptr; @@ -406,8 +413,8 @@ static void promote_globals(Context *ctx) { log_info("Promoting globals..\n"); - std::unordered_map clock_count, reset_count, cen_count; - for (auto net : ctx->nets) { + std::map clock_count, reset_count, cen_count; + for (auto net : sorted(ctx->nets)) { NetInfo *ni = net.second; if (ni->driver.cell != nullptr && !is_global_net(ctx, ni)) { clock_count[net.first] = 0; @@ -481,13 +488,19 @@ static void promote_globals(Context *ctx) // Main pack function bool pack_design(Context *ctx) { - pack_constants(ctx); - promote_globals(ctx); - pack_io(ctx); - pack_lut_lutffs(ctx); - pack_nonlut_ffs(ctx); - pack_ram(ctx); - return true; + try { + log_break(); + pack_constants(ctx); + promote_globals(ctx); + pack_io(ctx); + pack_lut_lutffs(ctx); + pack_nonlut_ffs(ctx); + pack_ram(ctx); + log_info("Checksum: 0x%08x\n", ctx->checksum()); + return true; + } catch (log_execution_error_exception) { + return false; + } } NEXTPNR_NAMESPACE_END diff --git a/ice40/pcf.cc b/ice40/pcf.cc index 756aba4a..87d27ff1 100644 --- a/ice40/pcf.cc +++ b/ice40/pcf.cc @@ -27,44 +27,51 @@ NEXTPNR_NAMESPACE_BEGIN // Read a w // Apply PCF constraints to a pre-packing design -void apply_pcf(Context *ctx, std::istream &in) +bool apply_pcf(Context *ctx, std::istream &in) { - if (!in) - log_error("failed to open PCF file"); - std::string line; - while (std::getline(in, line)) { - size_t cstart = line.find("#"); - if (cstart != std::string::npos) - line = line.substr(0, cstart); - std::stringstream ss(line); - std::vector words; - std::string tmp; - while (ss >> tmp) - words.push_back(tmp); - if (words.size() == 0) - continue; - std::string cmd = words.at(0); - if (cmd == "set_io") { - size_t args_end = 1; - while (args_end < words.size() && words.at(args_end).at(0) == '-') - args_end++; - std::string cell = words.at(args_end); - std::string pin = words.at(args_end + 1); - auto fnd_cell = ctx->cells.find(cell); - if (fnd_cell == ctx->cells.end()) { - log_warning("unmatched pcf constraint %s\n", cell.c_str()); + try { + if (!in) + log_error("failed to open PCF file"); + std::string line; + while (std::getline(in, line)) { + size_t cstart = line.find("#"); + if (cstart != std::string::npos) + line = line.substr(0, cstart); + std::stringstream ss(line); + std::vector words; + std::string tmp; + while (ss >> tmp) + words.push_back(tmp); + if (words.size() == 0) + continue; + std::string cmd = words.at(0); + if (cmd == "set_io") { + size_t args_end = 1; + while (args_end < words.size() && + words.at(args_end).at(0) == '-') + args_end++; + std::string cell = words.at(args_end); + std::string pin = words.at(args_end + 1); + auto fnd_cell = ctx->cells.find(cell); + if (fnd_cell == ctx->cells.end()) { + log_warning("unmatched pcf constraint %s\n", cell.c_str()); + } else { + BelId pin_bel = ctx->getPackagePinBel(pin); + if (pin_bel == BelId()) + log_error("package does not have a pin named %s\n", + pin.c_str()); + fnd_cell->second->attrs["BEL"] = + ctx->getBelName(pin_bel).str(); + log_info("constrained '%s' to bel '%s'\n", cell.c_str(), + fnd_cell->second->attrs["BEL"].c_str()); + } } else { - BelId pin_bel = ctx->getPackagePinBel(pin); - if (pin_bel == BelId()) - log_error("package does not have a pin named %s\n", - pin.c_str()); - fnd_cell->second->attrs["BEL"] = ctx->getBelName(pin_bel).str(); - log_info("constrained '%s' to bel '%s'\n", cell.c_str(), - fnd_cell->second->attrs["BEL"].c_str()); + log_error("unsupported pcf command '%s'\n", cmd.c_str()); } - } else { - log_error("unsupported pcf command '%s'\n", cmd.c_str()); } + return true; + } catch (log_execution_error_exception) { + return false; } } diff --git a/ice40/pcf.h b/ice40/pcf.h index e0816075..b86a7609 100644 --- a/ice40/pcf.h +++ b/ice40/pcf.h @@ -27,7 +27,7 @@ NEXTPNR_NAMESPACE_BEGIN // Apply PCF constraints to a pre-packing design -void apply_pcf(Context *ctx, std::istream &in); +bool apply_pcf(Context *ctx, std::istream &in); NEXTPNR_NAMESPACE_END diff --git a/ice40/picorv32_arachne.sh b/ice40/picorv32_arachne.sh index 00cfe6a4..b3960fdc 100755 --- a/ice40/picorv32_arachne.sh +++ b/ice40/picorv32_arachne.sh @@ -3,7 +3,7 @@ set -ex rm -f picorv32.v wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v yosys -p 'synth_ice40 -nocarry -blif picorv32.blif -top top' picorv32.v picorv32_top.v -arachne-pnr -d 8k --post-place-blif picorv32_place.blif picorv32.blif +arachne-pnr -d 8k --post-place-blif picorv32_place.blif picorv32.blif -o picorv32_arachne_all.asc yosys -p "read_blif -wideports picorv32_place.blif; read_verilog -lib +/ice40/cells_sim.v; write_json picorv32_place.json" ./transform_arachne_loc.py picorv32_place.json > picorv32_place_nx.json -../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32_place_nx.json +../nextpnr-ice40 --hx8k --asc picorv32_ar_placed.asc --json picorv32_place_nx.json --force